0

在“Python 的历史”系列的这篇博文中,Guido van Rossum 指出:

描述符的另一个增强功能是在类中引入了 __slots__ 属性。

我将这句话理解为:在引擎盖下 __slots__ 是由描述符实现的。

但与我的解释相反,Guido van Rossum 后来写了几行:

在幕后,此功能的实现完全在 C 中完成,并且非常高效。

那么,__slots__ 不是由描述符实现的吗?

但两句话之后,他又写道:

__slots__ 不仅是描述符的有趣应用,...

那么,__slots__ 和描述符的实际情况是什么?

__slots__ 是否由描述符实现?如果是的话:如何?

4

2 回答 2

3

这些陈述并不自相矛盾。定义的属性__slots__是所创建类的描述符,并且该描述符的实现是用 C 编写的(假设是 CPython)。

描述符类被调用member_descriptor,从这个示例代码可以看出:

import inspect

class Test:
    __slots__ = 'a',
    def __init__(self, a):
        self.a = a

type(Test.a)                        # member_descriptor
inspect.isdatadescriptor(Test.a)    # True
inspect.ismemberdescriptor(Test.a)  # True

在 GitHub 上的 CPython 存储库上快速搜索发现了它的 C 实现(CPython 版本 3.8.0 的链接)


更详细一点:

Python 类本质上是一个dict带有(很多)花里胡哨的东西。另一方面,有一些 Python-C 类使用 C-struct来实现 Python 类。这样的 C 结构比字典更快并且需要(显着)更少的内存,即使它只包含 Python 对象(基本上是一个包含对 Python 对象的引用的 C 数组)。

为了使“普通”Python 类可以受益于更快的访问和减少的内存占用__slots__。一个类__slots__将本质上转换为一个 C 结构。然而,为了使属性查找/设置/删除映射到相应的struct成员成为可能,需要某种翻译层(描述符)。中定义的成员的翻译层__slots__member_descriptor.

__slots__因此,当您在-class的实例上查找属性时,您将获得 amember_descriptor并且member_descriptor它将知道如何获取/设置/删除基础 C- 的成员struct

于 2019-12-04T20:27:53.643 回答
2

考虑一个简单的类:

class A:
    __slots__ = ('a',)

是什么a?这是一个描述符:

>>> type(A.a)
<class 'member_descriptor'>

值中的每个字符串__slots__都用于通过该名称创建一个具有member_descriptor值的类属性。

这意味着您可以(尝试)通过A.a.__get__

>>> a = A()
>>> a.a
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: a

分配给它A.a.__set__

>>> a.a = 7

并尝试再次访问它:)

>>> a.a
7

不能做的是尝试分配给实例上的任何其他属性:

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

的存在__slots__不仅会创建请求的类属性,还会阻止在实例上创建任何其他属性。

于 2019-12-04T20:23:46.807 回答