Чтобы сделать enum-класс полностью доступным только для чтения, все, что требуется, - это метакласс, использующий хук __setattr__
, который предотвращает все присвоения атрибутов . Поскольку метакласс присоединяется к классу после того, как он создан , нет проблем с назначением правильных перечисляемых значений.
Как и ответ Этана, я использую класс EnumMeta
в качестве основы для пользовательского метакласса:
from enum import EnumMeta, Enum
class FrozenEnumMeta(EnumMeta):
"Enum metaclass that freezes an enum entirely"
def __new__(mcls, name, bases, classdict):
classdict['__frozenenummeta_creating_class__'] = True
enum = super().__new__(mcls, name, bases, classdict)
del enum.__frozenenummeta_creating_class__
return enum
def __call__(cls, value, names=None, *, module=None, **kwargs):
if names is None: # simple value lookup
return cls.__new__(cls, value)
enum = Enum._create_(value, names, module=module, **kwargs)
enum.__class__ = type(cls)
return enum
def __setattr__(cls, name, value):
members = cls.__dict__.get('_member_map_', {})
if hasattr(cls, '__frozenenummeta_creating_class__') or name in members:
return super().__setattr__(name, value)
if hasattr(cls, name):
msg = "{!r} object attribute {!r} is read-only"
else:
msg = "{!r} object has no attribute {!r}"
raise AttributeError(msg.format(cls.__name__, name))
def __delattr__(cls, name):
members = cls.__dict__.get('_member_map_', {})
if hasattr(cls, '__frozenenummeta_creating_class__') or name in members:
return super().__delattr__(name)
if hasattr(cls, name):
msg = "{!r} object attribute {!r} is read-only"
else:
msg = "{!r} object has no attribute {!r}"
raise AttributeError(msg.format(cls.__name__, name))
class FrozenEnum(Enum, metaclass=FrozenEnumMeta):
pass
Выше приведено различие между атрибутами, которые уже доступны, и новыми атрибутами, для простоты диагностики , Он также блокирует удаление атрибута , что, вероятно, так же важно!
Он также предоставляет метакласс и базовый класс FrozenEnum
для перечислений; используйте это вместо Enum
.
Чтобы заморозить выборку Color
перечисления:
>>> class Color(FrozenEnum):
... red = 1
... green = 2
... blue = 3
...
>>> list(Color)
[, , ]
>>> Color.foo = 'bar'
Traceback (most recent call last):
# ...
AttributeError: 'Color' object has no attribute 'foo'
>>> Color.red = 42
Traceback (most recent call last):
# ...
Cannot reassign members.
>>> del Color.red
Traceback (most recent call last):
# ...
AttributeError: Color: cannot delete Enum member.
Обратите внимание, что все изменения атрибута запрещены, новые атрибуты не разрешены, а удаления также заблокированы. Когда имена являются членами enum, мы делегируем исходную обработку EnumMeta
для поддержания стабильности сообщений об ошибках.
Если ваше перечисление использует свойства, которые изменяют атрибуты в классе перечисления, вы должны либо внести их в белый список, либо разрешить установку имен, начинающихся с одного подчеркивания; в __setattr__
определите, какие имена будет разрешено устанавливать и использовать super().__setattr__(name, value)
для этих исключений, точно так же, как код теперь различает построение класса и более поздние изменения, используя атрибут flag.
Приведенный выше класс можно использовать точно так же, как Enum()
, чтобы программно создать перечисление:
e = FrozenEnum('Things', [('foo',1), ('bar',2)]))
Демонстрация:
>>> e = FrozenEnum('Things', [('foo',1), ('bar',2)])
>>> e
>>> e.foo = 'bar'
Traceback (most recent call last):
# ...
AttributeError: Cannot reassign members.
Подробный метод:
grep 'example.com' www_log > `date +example.com.%Y-%m-%d-%H:%M:%S.log`
Краткий метод:
grep 'example.com' www_log > `date +example.com.%F-%T.log`
grep 'example.com' www_log > example.com.$(date +%F-%T).log
Вот иначе, который я обычно использую:
grep 'example.com' www_log > example.com.`date +%F-%T`.log
Обратные галочки являются формой замены команды. Другая форма должна использовать $ ():
$(command)
который совпадает с:
`command`