如何像内置 classes 一样在 pre __init__ 状态下为 class 禁用 settattr?

How to disable settattr for your class in pre __init__ state just like build-in classes do?

因此,如果我在初始化之前使用内置列表尝试类似的操作:

list.hack = 'impossible'

我收到类型错误。

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

但是如果我像这样扩展内置列表 class:

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

我可以奇怪地做到这一点:

mylist.hack = 'haha'

当我初始化 "mylist" 时,我会在其中包含一个 "hack" 属性。

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

尽管我在初始化后无法设置任何新属性 "mylist",但我可以在预初始化状态下设置。

是否可以使用自定义 classes 获得与内置函数相同的预初始化行为?

首先,__setattr__ def 不是必需的:

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

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

您不是将属性添加到实例 (x),而是添加到 class (MyList)。 (有些语言有一个 static 关键字来表示这些 属性(C++、Java、PHP、...)

大致相当于:

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

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

注意这与pre/post init:

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

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

你有:

>>> x.hack
'haha'

还有:

>>> x.hack2
'hehe'

总而言之,在 Python 中:

  • 可以在class定义后添加class属性;
  • 如果 xC 的一个实例并且 attrC 的一个属性,那么 x.attr 等价于 C.attr

郑重声明,您可以使用 metaclass:

来防止这种灵活的行为
>>> 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

备注:

  • 除非您知道自己在做什么,否则不要这样做。
  • 不要使用它来保护您的 classes:可以轻松绕过此行为并添加 class 属性。

言论总结:不要这样做.