python - 如何使用 `__slots__` 进行属性初始化?

标签 python initialization slots

我通读了 usage of slots 上的主要答案它让我了解了如何以及在何处使用 __slots__

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

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

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

ValueError:__slots__ 中的“_fields”与类变量冲突

我把代码改成

class B(object):
    __slots__ = ('_fields')
    def __init__(self):
        _fields = set()

而且效果很好。我的问题是,它是否是正确的更改?

正如我从原始代码中得到的,我猜它是说不要使用 __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))

它给出了以下输出。

['__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__']

我们可以看到只有 C 对象显示了正确的足迹,即没有 __dict__ 而只有 __slots__ 。这不是我们理想中想要的吗?关于 __weakref__ 的任何解释也会有所帮助。

同样在 Python 2 上,B 和 C 对象显示相同的足迹。基于此,C 应该是正确的表达方式,因为它也在 Python 2 和 3 上编译。

最佳答案

But this is giving error Python 3 while working fine on Python 2:

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

虽然您在 Python2 中没有像在 Py3k 中那样在类创建/编译时遇到错误,但如果您尝试实际设置 _fields 的值,您会得到 AttributeError: 'C ' 对象属性 '_fields' 是只读的:

>>> 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
>>>

另见第四个note in the slots documentation :

__slots__ are implemented at the class level by creating descriptors (Implementing Descriptors) for each variable name. As a result, class attributes cannot be used to set default values for instance variables defined by __slots__; otherwise, the class attribute would overwrite the descriptor assignment.


根据您的修改:

I change the code to

class B(object):
    __slots__ = ('_fields')
    def __init__(self):
        _fields = set()

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

 class B(object):
    __slots__ = ('_fields')
    def __init__(self):
        self._fields = set()

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

 class B(object):
     __slots__ = ('_fields')
     def __init__(self, _fields=None):
         if not _fields:
             self._fields = set()

Wrt 进一步实验:

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

A 类没有,所以也有 __dict__

C 类有 __slots__ 正确。

关于python - 如何使用 `__slots__` 进行属性初始化?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41893267/

相关文章:

python - 'numpy.ndarray' object has no attribute 'barh' 是什么意思,如何纠正?

python - 如何从 Django 1.7 中的初始迁移迁移回来?

python - 计算整数的二进制数的程序 - 需要帮助反转输出字符串

Python 不能在函数定义之外使用 **

c - 初始化静态结构

c++ - 在函数内部使用 "new"

c++ - 用文字初始化引用成员变量

c++ - 对 SOME_SIGNAL_NAME 的 undefined reference

c++ - Qt - 实现自定义信号和槽的正确方法

javascript - django include_javascript/use_javascript 和类似的