【问题标题】:If an object doesn't have `__dict__`, does its class must have a `__slots__` attribute?如果一个对象没有`__dict__`,它的类是否必须有`__slots__`属性?
【发布时间】:2017-10-04 22:33:38
【问题描述】:

来自https://stackoverflow.com/a/1529099/156458

要支持任意属性分配,对象需要__dict__: 与对象关联的字典,其中可以是任意属性 存储。否则,无处可放新属性。

object 的实例不携带 __dict__ -- 如果有, 在可怕的循环依赖问题之前(因为__dict__,像大多数 其他一切,继承自object;-),这会让每个人都感到不安 Python 中带有 dict 的对象,这意味着很多开销 当前没有或不需要字典的每个对象的字节数 (本质上,所有不能任意分配的对象 属性没有或不需要字典)。

...

当类具有 __slots__ 特殊属性(字符串序列)时,class 语句(更准确地说,默认元类 type装备每个实例该类具有__dict__(因此能够具有任意属性),只是具有给定名称的一组有限的、刚性的“插槽”(基本上每个位置都可以包含对某个对象的一个​​引用)。

如果一个对象没有__dict__,那么它的类是否必须有__slots__ 属性?

例如object 的实例没有__dict__

>>> object().__dict__
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'object' object has no attribute '__dict__'

但是

>>> object.__slots__
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: type object 'object' has no attribute '__slots__'

那么为什么object 的实例没有__dict__object 没有__slots__ 属性?

object 的实例是否有任何属性?

有多少种可能性:

  • 一个对象有__dict__,它的类有__dict__但没有__slots__
  • 一个对象没有__dict__,它的类有__slots__
  • 一个对象没有__dict__,它的类也没有__slots__

是否可以判断一个对象是否有来自其类的__dict__

  • 如果它的类有__slots__,那么它就没有__dict__
  • 如果它的类没有__slots__,我怎么知道它是否有__dict__

【问题讨论】:

  • 偶尔看到一个非家庭作业的问题令人耳目一新。
  • 这些规则不一定适用于内置类或在扩展模块中用 C 实现的类。
  • 我想你只是证明了不,如果一个类型没有__dict__,则不需要__slots__
  • 注意__slots__可以在类创建时间后删除。
  • @o11c:嗯,是的,这给整个交易增加了一层潜在的误导。

标签: python python-3.x


【解决方案1】:

对于用户定义的类(在常规 Python 代码中使用 class 关键字定义),类将始终在类上具有 __slots__,在实例上始终具有 __dict__,或两者兼有(如果定义的插槽之一是'__dict__',或者继承链中的用户定义类之一定义了__slots__,而另一个没有,隐式创建__dict__)。这就是用户定义的类所涵盖的四种可能性中的三种。

编辑:更正:从技术上讲,一个用户定义的类could have neither;该类将使用__slots__ 定义,但在定义时间后将其删除(设置该类型的机器不需要__slots__ 在类定义完成后持续存在)。任何理智的人都不应该这样做,并且可能会产生不良的副作用(未经测试的完整行为),但这是可能的。

对于内置类型,至少在 CPython 参考解释器中,它们不可能有__slots__(如果有,那就是模拟用户定义的类,定义它实际上并没有任何有用的事情)。内置类型通常将其属性存储为原始 C 级值和 C 级结构上的指针,可选地使用显式创建的描述符或访问器方法,这消除了 __slots__ 的用途,这只是一个方便的有限用途等价物为用户定义的类构建游戏。 __dict__ 是内置类型的选择加入,而不是默认启用(尽管选择加入过程相当简单;您需要在结构中的某处放置一个 PyObject* 条目并在类型中为其提供偏移量定义)。

需要明确的是,__dict__ 不需要出现在 class 上,它会出现在其 instances 中; __slots__是类级别,可以抑制instance上的__dict__,但对类本身是否有__dict__没有影响;用户定义的总是有__dict__,但如果你小心使用__slots__,它们的实例不会。

简而言之:

(Sane)用户定义的类至少有__dict__(在实例上)或__slots__(在类上)之一,并且可以同时拥有。疯狂的用户定义类可能两者都没有,但只有精神错乱的开发人员才会这样做。

内置类通常两者都没有,可能提供__dict__,并且几乎从不提供__slots__,因为这对它们毫无意义。

例子:

# Class has __slots__, instances don't have __dict__
class DictLess:
    __slots__ = ()

# Instances have __dict__, class lacks __slots__
class DictOnly:
    pass

# Class has __slots__, instances have __dict__ because __slots__ declares it
class SlottedDict:
    __slots__ = '__dict__',

# Class has __slots__ without __dict__ slot, instances have it anyway from unslotted parent
class DictFromParent(DictOnly):
    __slots__ = ()

# Complete insanity: __slots__ takes effect at class definition time, but can
# be deleted later, without changing the class behavior:
class NoSlotNoDict:
    __slots__ = ()
del NoSlotNoDict.__slots__
# Instances have no __dict__, class has no __slots__ but acts like it does
# (the machinery to make it slotted isn't undone by deleting __slots__)
# Please, please don't actually do this

# Built-in type without instance __dict__ or class defined __slots__:
int().__dict__  # Raises AttributeError
int.__slots__   # Also raises AttributeError

# Built-in type that opts in to __dict__ on instances:
import functools
functools.partial(int).__dict__ # Works fine
functools.partial.__slots__ # Raises AttributeError

【讨论】:

  • 谢谢。是否可以仅从其类中判断一个对象是否具有__dict__?如果它的类有__slots__,那么它就没有__dict__。如果它的类没有__slots__,我怎么知道它有没有__dict__
  • @Tim: hasattr(obj, '__dict__')?
  • @Tim:具有__slots__ 的类仍然可以具有__dict__(如果它包含'__dict__' 作为插槽名称,或者父类未定义__slots__)。如果类没有__slots__,它可能是没有__dict__ 的内置类型,或者是带有__dict__ 的用户定义类。做一个实例,检查__dict__,这是可靠的方法。
  • by "一个有__slots__的类仍然可以有__dict__",你的意思是“如果一个类有__slots__,它的实例仍然可以有__dict__”还是“如果一个类有__slots__,类本身仍然可以有__dict__" ?
  • @Tim:用户定义的类本身总是有一个__dict__;您可以使用 __slots__ 在实例上抑制它。
猜你喜欢
  • 2019-01-19
  • 2011-11-21
  • 2019-12-02
  • 2010-12-27
  • 2017-12-20
  • 2017-12-29
  • 1970-01-01
  • 2010-11-23
  • 1970-01-01
相关资源
最近更新 更多