为什么使用描述符可以引入 __slots__?
Why the introduction of __slots__ was made possible with descriptors?
在系列 "The History of Python" 的 this blog post 中,Guido van Rossum 指出:
Another enhancement made possible with descriptors was the introduction of the __slots__ attribute on classes.
我对这句话的理解是:under the hood __slots__ is implemented by descriptors.
但与我的解释相反,Guido van Rossum 在后面写了几行:
Underneath the covers, the implementation of this feature is done entirely in C and is highly efficient.
那么,__slots__不是描述符实现的吗?
但是两句话之后,他又写道:
Not only was __slots__ an interesting application of descriptors, ...
那么,__slots__ 和描述符的实际情况如何?
__slots__ 是否由描述符实现?如果是:如何?
考虑一个简单的 class:
class A:
__slots__ = ('a',)
什么是a
?这是一个描述符:
>>> type(A.a)
<class 'member_descriptor'>
__slots__
值中的每个字符串用于创建具有 member_descriptor
值的 class 属性。
这意味着您可以(尝试)通过 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__
的存在不仅创建请求的 class 属性,而且 阻止 在实例上创建任何其他属性。
这些说法并不自相矛盾。 __slots__
定义的属性是创建的 class 上的描述符 和 该描述符的实现是用 C 编写的(假设 CPython)。
描述符 class 被称为 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 存储库中快速搜索显示 the C implementation of it (Link for CPython version 3.8.0)。
更详细一点:
A Python class 本质上是 dict
带有(很多)花里胡哨的东西。另一方面,有 Python-C-classes 使用 C-struct
来实现 Python class。这样的 C 结构比字典更快并且需要(显着)更少的内存,即使它只包含 Python 个对象(它基本上是一个包含对 Python 个对象的引用的 C 数组)。
为了使 "normal" Python classes 可以从更快的访问和减少的内存占用中受益,引入了 __slots__
。 class 和 __slots__
本质上将被转换为 C 结构。然而,为了使属性 lookup/setting/deletion 映射到相应的 struct
成员成为可能,需要某种翻译层(描述符)。 __slots__
中定义的成员的转换层是 member_descriptor
。
因此,当您在 __slots__
-class 的实例上查找属性时,您将得到 member_descriptor
并且 member_descriptor
将知道如何 get/set/delete底层C成员-struct
.
在系列 "The History of Python" 的 this blog post 中,Guido van Rossum 指出:
Another enhancement made possible with descriptors was the introduction of the __slots__ attribute on classes.
我对这句话的理解是:under the hood __slots__ is implemented by descriptors.
但与我的解释相反,Guido van Rossum 在后面写了几行:
Underneath the covers, the implementation of this feature is done entirely in C and is highly efficient.
那么,__slots__不是描述符实现的吗?
但是两句话之后,他又写道:
Not only was __slots__ an interesting application of descriptors, ...
那么,__slots__ 和描述符的实际情况如何?
__slots__ 是否由描述符实现?如果是:如何?
考虑一个简单的 class:
class A:
__slots__ = ('a',)
什么是a
?这是一个描述符:
>>> type(A.a)
<class 'member_descriptor'>
__slots__
值中的每个字符串用于创建具有 member_descriptor
值的 class 属性。
这意味着您可以(尝试)通过 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__
的存在不仅创建请求的 class 属性,而且 阻止 在实例上创建任何其他属性。
这些说法并不自相矛盾。 __slots__
定义的属性是创建的 class 上的描述符 和 该描述符的实现是用 C 编写的(假设 CPython)。
描述符 class 被称为 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 存储库中快速搜索显示 the C implementation of it (Link for CPython version 3.8.0)。
更详细一点:
A Python class 本质上是 dict
带有(很多)花里胡哨的东西。另一方面,有 Python-C-classes 使用 C-struct
来实现 Python class。这样的 C 结构比字典更快并且需要(显着)更少的内存,即使它只包含 Python 个对象(它基本上是一个包含对 Python 个对象的引用的 C 数组)。
为了使 "normal" Python classes 可以从更快的访问和减少的内存占用中受益,引入了 __slots__
。 class 和 __slots__
本质上将被转换为 C 结构。然而,为了使属性 lookup/setting/deletion 映射到相应的 struct
成员成为可能,需要某种翻译层(描述符)。 __slots__
中定义的成员的转换层是 member_descriptor
。
因此,当您在 __slots__
-class 的实例上查找属性时,您将得到 member_descriptor
并且 member_descriptor
将知道如何 get/set/delete底层C成员-struct
.