Рубрики
Без рубрики

Enum VS Флаг для растровых компаний в Python

Вы когда-нибудь задавались вопросом, что за этими забавными значениями разрешений доступа UNIX, такие как 600 ✋, 777 ⚠️, или … с меткой Python, Binary, Bitmask.

Вы когда-нибудь задавались вопросом, что за этими забавными значениями разрешений доступа UNIX, такие как 600 ✋, 777 ⚠️, или 666 😈? Ну, они Октальные представления , где каждая из трех крайних главных цифр представляет собой разную часть разрешений для владельца, группы и других.

Каждая из этих цифр создана из суммы его компонентных битов. В результате конкретные биты добавляют в сумму, так как она представлена цифрой:

  • читать бит добавляет 4 до его общего (в двоичном 100 ),
  • Напишите бит добавляет 2 до его общего (в двоичном 010 ), а также
  • Выполнить Бит добавляет 1 до его общего (в бинарном 001 ).

Эти значения никогда не вызывают неоднозначные комбинации и поэтому каждая сумма представляет собой уникальный набор разрешений. Давайте возьмем 600 Например. Первая цифра 6 представляет разрешения для владельца. В бинарном это 110 И единственный способ создать его из суммы компонентных битов выше, добавляя 010 ( Написать Доступ) до 100 ( Читать Доступ). Другими словами, владелец имеет разрешение на чтение и запись файла, но не выполнять его.

Использование этой системы для разрешений оптимизирована для хранения данных. Но также токарные разрешения на (или выключенные) могут быть сделаны эффективно в том, что известно как бит-маскировка.

Bitmasks

Вы видите, чтобы включить определенные биты, Или Оператор может быть использован. Этот оператор следует правило, что Х или и Икс ИЛИ . Если из примера выше мы сейчас также хотим владельца иметь Исполнительный Разрешение на файл, мы просто используем Или С 1 ( execute Value) на текущем значении разрешений: 6 или или в бинарном

   110
OR
   001
=  111

Мы также можем выключить определенные биты, используя И Оператор, который следует правило, что Икс И и Икс И Отказ Чтобы отключить немного, мы используем его с 0 Отказ Интересно, что мы можем оставить немного, как это было при использовании 1 Отказ

Включение и выключение битов также называют «маскировкой» и «маскировать».

Используя эту очень старую технику для хранения частей разрешения очень круто! Но есть ли другие случаи использования, где имеет смысл? Их все еще есть сегодня, хотя они стали непопулярными из-за гораздо более эффективного оборудования, позволяющего использовать набор или словарь для хранения представлений, которые в противном случае удержали бы.

Фильтрация с использованием битовой маскировки

Для Бит Проект Мне нужен способ сохранить поддерживаемые функции для каждого из многочисленных объектов, представляющих сетевые API. Вместо использования словаря, чтобы сопоставить каждый API к его поддерживаемому набору функций или хранить набор функций на каждом API, я решил использовать систему, аналогичную значениям разрешения доступа UNIX.

Использование битовой маскировки позволяет быстро фильтрацию API в зависимости от определенных особенностей. Это может быть достигнуто с помощью единого И оператор. Мы просто используем значение фильтра Y Это представляет функции, которые мы хотим фильтровать, и значение Х Представление поддерживаемых функций некоторых объектов API. Расчет Икс И у дает еще одно значение Z . Но вспомните, что каждый бит в Z будет либо 0, если бит в Y было 0, или это будет немного Х сам.

Можете ли вы увидеть тонкое последствие от этого? Если N-бит нашей фильтрации Y было 0, то тогдашний бит результата Z будет 0. Нет потери информации, потому что нам все равно, если битовая ценность в Х сигнализировала поддержка этой функции или нет. Но если N-бит Y было 1, тогда мы также хотим N’th But of Z быть 1. Это возможно, только если соответствующий бит в Х Был 1, отсюда для поддержки сигнализации этой функции. Таким образом, мы можем сравнить результат Z к Y сам. Если Х и у Тогда и только тогда будет ценность Х сигнал для (по крайней мере) все функции, которые мы фильтруем со значением Y .

Давайте возьмем быстрый пример, используя значения разрешений UNIX еще раз. Допустим, у нас есть список файлов с именами разрешений и захотете фильтровать их, чтобы сохранить только те, которые нам разрешено читать , что является значением 4 или в двоичном 100 Отказ Одним из файлов может иметь значение разрешения 5, которое в двоичном является 101 И таким образом соответствует читать и Выполнить разрешение.

Фильтр, чтобы увидеть, если хотя бы читать Разрешение было установлено в этом файле, будет проверено следующим образом:

    101
AND
    100
=   100

Мы видим, что результат на последней строке точно такое же значение, как наш фильтр. Это так же, как мы ожидали, и поэтому мы будем держать его.

Если файл дает нам разрешение на Напишите и Выполнить , но не к читать он представлен как 3 или в бинарном двоике 011 Отказ Применение нашего фильтра:

    011
AND
    100
=   000

Результат отличается от нашего фильтра, и поэтому мы немедленно знаем, что фильтр не был удовлетворен.

Настоящая красота происходит от применения сложных фильтров с включенными несколькими битами. Если бы мы хотели фильтровать для E.G. читать и Выполнить Разрешения, фильтр будет 101 И так же, как до того, как мы проверили, если финал И Результат будет равен этой ценности.

Значение Z в результате использования И Оператор в разрешении файла и фильтрации – это пересечение разрешений в файле и фильтре.

Битовая фильтрация в Python

Использование Python 3.6+

С момента Python 3.6 класс Флаг существует для операций битовой маски.

>>> from enum import IntFlag, auto
>>>
>>> class Permissions(IntFlag):
...    EXECUTE = auto()
...    WRITE = auto()
...    READ = auto()
...
>>> class PermissionSet:
...    def __init__(self, *flags):
...        self._permission = Permissions(0)  # Initiate no permissions
...        for flag in flags:
...            self._permission |= Permissions[flag.upper()]
...    def __contains__(self, item):
...        return (self._permission & item._permission) == item._permission

Мы можем использовать это Permissionset Класс для хранения разрешений файла следующим образом:

>>> class File:
...    def __init__(self, name, *, permission):
...        self.name = name
...        self._permissions = PermissionSet(*permission)

Давайте использовать его в действии!

>>> # A filter:
>>> permission_filter = PermissionSet("read", "execute")
>>>
>>> # The dummy files:
>>> files = []
>>> files.append(File("file_rw", permission=("read", "write")))              # Should be excluded from filter
>>> files.append(File("file_re", permission=("read", "execute")))            # Should be included from filter
>>> files.append(File("file_w", permission=("read", "execute")))             # Should be included from filter
>>> files.append(File("file_we", permission=("write", "execute")))           # Should be excluded from filter
>>> files.append(File("file_rwe", permission=("read", "write", "execute")))  # Should be included from filter
>>>
>>> # Filter the files:
>>> for f in filter(lambda f: permission_filter in f._permissions, files):
...    print(f.name)
...
'file_re'
'file_w'
'file_rwe'

Использование Python 3.5.

Однако перед Python 3.6 это может быть достигнуто с использованием Intenum Класс, однако с ручной регулировкой базовых значений разрешений.

>>> from enum import IntEnum
>>>
>>> class Permissions(IntEnum):
...    EXECUTE = 1 << 0
...    WRITE = 1 << 1
...    READ = 1 << 2
...
>>> class PermissionSet:
...    def __init__(self, *flags):
...        self._permission = 0  # Initiate no permissions
...        for flag in flags:
...            self._permission |= Permissions[flag.upper()]
...    def __contains__(self, item):
...        return (self._permission & item._permission) == item._permission

Затем мы можем использовать его точно так же, как мы уже сделали выше в примере для Infflag Отказ

Что вы думаете об этой технике? Это элегантно? Или чрезмерная техника?

Оригинал: “https://dev.to/bjarnemagnussen/enum-vs-flag-for-bitmasks-in-python-2ig8”