【问题标题】:Why the introduction of __slots__ was made possible with descriptors?为什么使用描述符可以引入 __slots__?
【发布时间】:2019-12-04 20:17:18
【问题描述】:

在“Python 的历史”系列的this blog post 中,Guido van Rossum 说:

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

我把这句话理解为:在底层,__slots__ 是由描述符实现的。

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

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

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

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

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

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

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

【问题讨论】:

    标签: python slots python-descriptors


    【解决方案1】:

    这些陈述并不自相矛盾。 __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 存储库发现 the C implementation of it (Link for CPython version 3.8.0)


    更详细一点:

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

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

    因此,当您在 __slots__-class 的实例上查找属性时,您将获得 member_descriptor 并且 member_descriptor 将知道如何获取/设置/删除底层 C-@ 的成员987654335@.

    【讨论】:

      【解决方案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__ 的存在不仅创建了请求的类属性,而且防止在实例上创建任何其他属性。

      【讨论】:

        猜你喜欢
        • 2012-12-24
        • 2021-04-08
        • 2020-08-12
        • 2015-04-18
        • 2014-04-24
        • 1970-01-01
        • 2015-06-08
        相关资源
        最近更新 更多