Перечисление Python предотвращает неправильное присвоение атрибута

Вот простое решение, красноречие имеет whereDate и whereMonth методы,

{{ $user->attendances()->whereMonth('created_at',\Carbon\Carbon::now()->format('m') )->count() }} 

Надеюсь, что это поможет

2
задан aaa90210 20 January 2019 в 05:59
поделиться

2 ответа

1119 Не обязательно легко, но возможно. Нам нужно создать новый тип EnumMeta 1 , создать Enum обычным образом, а затем переназначить тип после создания Enum:

from enum import Enum, EnumMeta

class FrozenEnum(EnumMeta):
    "prevent creation of new attributes"
    def __getattr__(self, name):
        if name not in self._member_map_:
            raise AttributeError('%s %r has no attribute %r'
                % (self.__class__.__name__, self.__name__, name))
        return super().__getattr__(name)

    def __setattr__(self, name, value):
        if name in self.__dict__ or name in self._member_map_:
            return super().__setattr__(name, value)
        raise AttributeError('%s %r has no attribute %r'
                % (self.__class__.__name__, self.__name__, name))

class Color(Enum):
    red = 1
    green = 2
    blue = 3

Color.__class__ = FrozenEnum

и использования :

>>> type(Color)
<class 'FrozenEnum'>

>>> list(Color)
[<Color.red: 1>, <Color.green: 2>, <Color.blue: 3>]

>>> Color.blue
<Color.blue: 3>

>>> Color.baz = 3
Traceback (most recent call last):
  ...
AttributeError: FrozenEnum 'Color' has no attribute 'baz'

>>> Color.baz
Traceback (most recent call last):
  ...
AttributeError: 'FrozenEnum' object has no attribute 'baz'

Попытка переназначения члена по-прежнему приводит к более дружественной ошибке:

>>> Color.blue = 9
Traceback (most recent call last):
  ...
AttributeError: Cannot reassign members.

Чтобы немного переназначить класс, мы можем написать декоратор для инкапсуляции процесса. :

def freeze(enum_class):
    enum_class.__class__ = FrozenEnum
    return enum_class

и используется:

@freeze
class Color(Enum):
    red = 1
    green = 2
    blue = 3

Обратите внимание, что все еще можно перезаписать обычные атрибуты, такие как функции:

@freeze
class Color(Enum):
    red = 1
    green = 2
    blue = 3
    def huh(self):
        print("Huh, I am %s!" % self.name)

и используемые :

>>> Color.huh
<function Color.huh at 0x7f7d54ae96a8>

>>> Color.blue.huh()
Huh, I am blue!

>>> Color.huh = 3
>>> Color.huh
3
>>> Color.blue.huh()
Traceback (most recent call last):
  ...
TypeError: 'int' object is not callable

Даже это может быть заблокировано, но я оставлю это (пока) в качестве упражнения для кого-то еще.


1 Это только второй случай, который я видел, когда требуется подкласс EnumMeta. О других см. this question .

Раскрытие: я являюсь автором Python stdlib Enum , enum34 backport и Advanced Enumeration (aenum) библиотека.

0
ответ дан Martijn Pieters 20 January 2019 в 05:59
поделиться

Чтобы сделать 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.red: 1>, <Color.green: 2>, <Color.blue: 3>]
>>> 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
<enum 'Things'>
>>> e.foo = 'bar'
Traceback (most recent call last):
    # ...
AttributeError: Cannot reassign members.
0
ответ дан Martijn Pieters 20 January 2019 в 05:59
поделиться
Другие вопросы по тегам:

Похожие вопросы: