Как отключить setattr для вашего класса в состоянии pre __init__, как это делают встроенные классы? - PullRequest
0 голосов
/ 05 мая 2020

Итак, если я попробую что-то вроде этого со встроенным списком перед инициализацией:

list.hack = 'impossible'

, я получу TypeError.

TypeError: can't set attributes of built-in/extension type 'list'

Но если я сделаю свой класс, который расширяется, собранным -в списке вот так:

class mylist(list):
    def __setattr__(self, name, value):
        raise NotImplementedError

Как ни странно, я могу это сделать:

mylist.hack = 'haha'

И когда я инициализирую «mylist», у меня будет атрибут «взломать».

x = mylist()
x.hack
[Out]: 'haha'

Несмотря на то, что я не могу установить какие-либо новые атрибуты после инициализации "mylist", я могу сделать это в состоянии до инициализации.

Можно ли получить такое же поведение до инициализации с кастомными классами, как там со встроенными?

1 Ответ

1 голос
/ 05 мая 2020

Прежде всего, __setattr__ def не требуется:

>>> class MyList(list):
...     pass

>>> MyList.hack = 'haha'
>>> x = MyList()
>>> x.hack
'haha'

Вы добавляете атрибут не к экземпляру (x), а к классу (MyList). (В некоторых языках есть ключевое слово static для этих атрибутов (C ++, Java, PHP, ...).)

Это примерно эквивалентно:

>>> class MyList(list):
...     hack = 'haha' # class attribute, ie. "static"

>>> x = MyList()
>>> x.hack
'haha'

Обратите внимание, что это не имеет ничего общего с предварительной / последующей инициализацией:

>>> class MyList(list):
...     hack = 'haha'

>>> x = MyList()
>>> MyList.hack2 = 'hehe' # post init

У вас есть:

>>> x.hack
'haha'

Но также:

>>> x.hack2
'hehe'

Подводя итог, в Python:

  • вы можете добавить атрибуты класса после определения класса;
  • если x является экземпляром C и attr атрибутом C , то x.attr эквивалентно C.attr.

Для записи вы можете предотвратить это гибкое поведение с помощью метакласса:

>>> class MyListMeta(type):
...     def __setattr__(self, name, value):
...         raise AttributeError()

>>> class MyList(list, metaclass=MyListMeta):
...     hack = 'haha'

Как и ожидалось:

>>> x = MyList()
>>> x.hack
'haha'

Но теперь:

>>> MyList.hack2 = 'hehe'
Traceback (most recent call last):
...
AttributeError

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

>>> MyList.hack = 'hehe'
Traceback (most recent call last):
...
AttributeError

Примечания:

  • Не делайте этого, если вы не знаете, что делаете.
  • Не используйте это для защиты ваших классов: это поведение можно легко обойти и добавить атрибуты класса.

Резюме из замечаний: не делать это .

...