我通读了 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/