*为什么* object() 不支持 `setattr`,但派生类却支持?

Hyp*_*ane 7 python python-3.x

今天我偶然发现了以下行为:

\n
class myobject(object):\n    """Should behave the same as object, right?"""\n\nobj = myobject()\nobj.a = 2        # <- works\nobj = object()\nobj.a = 2        # AttributeError: \'object\' object has no attribute \'a\'\n
Run Code Online (Sandbox Code Playgroud)\n

我想知道设计这种语言背后的逻辑是什么,因为这对我来说完全是自相矛盾的。它打破了我的直觉,如果我创建一个子类,不加修改,它的行为应该与父类相同。

\n
\n

编辑:很多答案表明这是因为我们希望能够编写可以使用的类,__slots__而不是__dict__出于性能原因。然而,我们可以这样做:

\n
class myobject_with_slots(myobject):\n    __slots__ = ("x",)\n    \nobj = myobject_with_slots()\nobj.x = 2\nobj.a = 2\nassert "a" in obj.__dict__      # \xe2\x9c\x94\nassert "x" not in obj.__dict__  # \xe2\x9c\x94\n
Run Code Online (Sandbox Code Playgroud)\n

所以看起来我们可以同时拥有两者,那么为什么不允许__slots__两者同时存在,但一对一的子类却允许呢?__dict__object

\n

Ale*_*all 5

考虑这段代码:

class A:
    __slots__ = ()

class B(A):
    __slots__ = ("x", "y")

b = B()
b.z = 1  # AttributeError
Run Code Online (Sandbox Code Playgroud)

__slots__ = ("x", "y")意味着B实例没有 a__dict__并且只能有属性xy。这对性能有好处。

如果删除__slots__from A,则A实例将获得 a __dict__,这意味着它们的子类也会获得 a ,特别是B__slots__这降低了提高性能的有效性。

因为object是所有类的超类,所以你可以像这里一样考虑它A,拥有__slots__ = ()和没有__dict__,这样其他类也可以避免拥有 a__dict__并充分受益于 custom __slots__


Mis*_*agi 4

因为派生类不一定支持setattr其中任何一个。

\n
class myobject(object):\n    """Should behave the same as object!"""\n    __slots__ = ()\n\nobj = myobject()\nobj.a = 2        # <- works the same as for object\n
Run Code Online (Sandbox Code Playgroud)\n

由于所有类型都派生自object,因此大多数内置类型(例如 )list也是示例。

\n

object子类可能支持任意属性分配,但并非所有子类都支持。因此,公共基类也不支持这一点

\n
\n

对任意属性的支持通常由所谓的__dict__槽支持。这是一个固定属性,包含文字dict 1来存储任何属性-值对。

\n

事实上,我们可以手动定义__dict__槽来获得任意属性支持。

\n
class myobject(object):\n    """Should behave the same as object, right?"""\n    __slots__ = ("__dict__",)\n\nobj = myobject()\nobj.a = 2            # <- works!\nprint(obj.__dict__)  # {\'a\': 2}\n
Run Code Online (Sandbox Code Playgroud)\n

这个演示的要点是固定属性实际上是 Python 的“基本行为”;当需要时,任意属性支持构建在顶部。

\n

默认情况下为子类型添加任意属性object可提供更简单的编程体验。但是,仍然支持object子类型的固定属性可以实现更好的内存使用和性能。

\n
\n

数据模型:__slots__

\n

__slots__过度使用所节省的空间__dict__可能是巨大的。属性查找速度也可以显着提高。

\n
\n

请注意,可以定义具有固定属性和任意属性的类。固定属性将受益于改进的内存布局和性能;由于它们不存储在 中__dict__,因此其内存开销2较低 \xe2\x80\x93 但仍然有成本。

\n
\n

1 Python 实现可以使用不同的优化类型,只要__dict__它们的行为类似于dict.

\n

2为了使基于散列的查找高效工作且很少发生冲突,adict必须于其存储的项目数。

\n