`vars(self)` 的响应缺少属性

response of `vars(self)` is missing attributes

我定义了一个class,需要把它的大部分属性交给函数处理。所以我想与其制造一团糟并给它们全部命名,不如 processSomething(vars(self)) 并交出一本包含所有属性和值的漂亮字典。

但是我发现几乎所有的属性都缺失了。

我已停止调试器中的代码并运行进行了一些测试:

>>> vars(self).keys()
dict_keys(['date_expire', 'tokenUrl', 'settings', 'requestSession', 'ttlDays'])

这些是 5 个。虽然我期望有大约 20 个属性,但根据我的 class 此处的 __slots__ 定义:

__slots__ = (
    'token',        # the invitation-token to load the username with
    'request',      # the http-request which is currently being serviced
    'inhibit',      
     #... many lines deleted
    'templateObj',  # an instance of myMail.template.Template
    'emailBody',    # what will be sent via SMTP
    'warningsAr',   # messages for the client
)

我可以在调试器中看到属性 window 并且可以直接访问它们。我已阅读 manual of vars(),但找不到任何开关。有趣的是,dir(self) 显示所有属性名称,但没有值。所以我不能使用它。但我认为 vars 和 dir 应该显示相同?

我想我会构建一个解决方法,但我真的很想了解这里发生了什么。你能帮忙吗?

vars() return您是实例的 __dict__ 名称space。但是 __slots__ 属性 未存储在 __dict__ 名称中 space。这就是他们的全部观点。

相反,Python 在实例内存结构中为每个值创建专用指针槽,并在 class 对象上使用 描述符 来检索这些值.所以 inst.attr 被翻译成 type(inst).attr.__get__(inst) 到 return 这个值。

来自__slots__ documentation:

The __slots__ declaration takes a sequence of instance variables and reserves just enough space in each instance to hold a value for each variable. Space is saved because __dict__ is not created for each instance.

[...]

  • __slots__ are implemented at the class level by creating descriptors (Implementing Descriptors) for each variable name.

请注意,拥有 __dict__ 无论如何 通常是您忘记为子 class 使用 __slots__ 属性的标志,或者您继承自未使用 __slots__ 本身的基础 Python class。正确使用 __slots__ 应该会导致没有 __dict__ namespace 的实例;他们的目标是通过避免字典所需的稀疏散列 table 来减少内存(这会浪费 space)。

再次来自文档:

  • When inheriting from a class without __slots__, the __dict__ attribute of that class will always be accessible, so a __slots__ definition in the subclass is meaningless.

[...]

  • The action of a __slots__ declaration is limited to the class where it is defined. As a result, subclasses will have a __dict__ unless they also define __slots__ (which must only contain names of any additional slots).

如果您想列出所有可用的实例字段,您必须包括 class 中的 __slots__ 枚举,而不仅仅是查看 vars():

from itertools import chain

def slots_for_instance(inst):
    def _slots_for_class(c):
        slots = getattr(c, '__slots__', ())
        if isinstance(slots, str):
            # __slots__ can be a string naming a single attribute
            slots = (slots,)
    return set(chain.from_iterable(
        getattr(_slots_for_class(c) for c in type(inst).__mro__))