Вы когда-нибудь задавались вопросом, что за этими забавными значениями разрешений доступа 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”