如何使用 `__slots__` 初始化属性?

ViF*_*iFI 7 python initialization slots

我通读了有关插槽使用的主要答案,它让我了解了如何以及在何处使用__slots__.

现在,我将代码从 Python 2 移植到 Python 3,类似于以下内容:

class B(object):
    __slots__ = ('_fields')
    _fields = set()
Run Code Online (Sandbox Code Playgroud)

但这会在 Python 2 上正常工作时给出错误 Python 3:

ValueError: '_fields' in __slots__ conflicts with class variable.

我将代码更改为

class B(object):
    __slots__ = ('_fields')
    def __init__(self):
        _fields = set()
Run Code Online (Sandbox Code Playgroud)

它工作正常。我的疑问是,它甚至是正确的更改吗?

正如我从原始代码中得到的,我猜它是说不要__dict__用于节省内存或更快的访问或任何原因,但同时也试图将属性类型指定_field为 set()。上面的更改是否是正确的表达方式,或者它可能会产生一些副作用。


进一步实验:我进一步实验了以下变体(在 Python 3 上):

import pdb

class A(object):
    a = set()

'''
class B(object):
    __slots__ = ('a')
    a = set()
'''

class C(object):
    __slots__ = ('a')
    def __init__(self):
        a = set()

class D(object):
    def __init__(self):
        __slots__ = ('a')
        a = set()

if __name__ == '__main__':
    #pdb.set_trace()
    x = A(); print(dir(x))
    #y = B()
    z = C(); print(dir(z))
    z1 = D(); print(dir(z1))
Run Code Online (Sandbox Code Playgroud)

它给出了以下输出。

['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'a']


['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__slots__', '__str__', '__subclasshook__', 'a']


['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__']
Run Code Online (Sandbox Code Playgroud)

我们可以看到只有 C 对象显示正确的足迹,即 no__dict__和 only __slots__。这不是我们理想中想要的吗?任何解释__weakref__也将有所帮助。

同样在 Python 2 上,B 和 C 对象都显示相同的足迹。基于这一点,C 应该是正确的放置方式,因为它也在 Python 2 和 3 上编译。

ane*_*oid 6

但这会在 Python 2 上正常工作时给出错误 Python 3:

ValueError: '_fields' in __slots__ conflicts with class variable.

虽然您在 Python2 中没有像在 Py3k 中那样在类创建/编译时收到错误,但如果您尝试实际设置 的值_fields,您会得到AttributeError: 'C' object attribute '_fields' is read-only

>>> class C(object):
...   __slots__ = ('_fields')
...   _fields = set()
...
>>>
>>> c = C()
>>> c._fields
set([])
>>> c._fields = 'foo'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'C' object attribute '_fields' is read-only
>>>
Run Code Online (Sandbox Code Playgroud)

另外,请参阅插槽文档中的第四个注释

__slots__通过为每个变量名称创建描述符(实现描述符)在类级别实现。因此,类属性不能用于为由__slots__;定义的实例变量设置默认值;否则,类属性将覆盖描述符分配。


写你的修改:

我将代码更改为

class B(object):
    __slots__ = ('_fields')
    def __init__(self):
        _fields = set()
Run Code Online (Sandbox Code Playgroud)

修改后的类 B 有一个_fieldsinside __init__(),而不是self._fields它只是 init 中的一个局部变量,并且不能在类中的任何其他地方访问。将其更改为:

 class B(object):
    __slots__ = ('_fields')
    def __init__(self):
        self._fields = set()
Run Code Online (Sandbox Code Playgroud)

要正确初始化_fields,请执行以下操作:

 class B(object):
     __slots__ = ('_fields')
     def __init__(self, _fields=None):
         if not _fields:
             self._fields = set()
Run Code Online (Sandbox Code Playgroud)

进一步的实验:

在 D 类中,__slots__是仅在__init()__. 它不是(特殊)类变量D.__slots__;甚至实例变量self.__slots__。所以它有__dict__

A 类没有,所以也有__dict__

C类__slots__正确。