我通读了usage of slots上的主要答案,并让我了解了如何以及在何处使用__slots__
。
现在,我正在将Python 2中的代码移植到Python 3,类似于以下内容:
class B(object):
__slots__ = ('_fields')
_fields = set()
但是这在Python 2上工作正常时给出了错误Python 3:
ValueError: '_fields' in __slots__ conflicts with class variable
。
我将代码更改为
class B(object):
__slots__ = ('_fields')
def __init__(self):
_fields = set()
它工作正常。我的疑问是,它是否是正确的变化?
正如我从原始代码中得到的那样,我想这是说不要使用__dict__
来保存内存或更快的访问权限或任何原因,但同时也试图指定属性类型{{ 1}} as set()。上述变化可以说是正确的方式,也可能会产生一些副作用。
进一步的实验: 我进一步尝试了以下变体(在Python 3上):
_field
它给出了以下输出。
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))
我们可以看到只有C对象显示正确的足迹,即没有['__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__']
而只有__dict__
。难道不是我们想要的理想选择吗?对__slots__
的任何解释也会有所帮助。
同样在Python 2上,B和C对象都显示相同的足迹。基于此,C应该是正确的方式,因为它也在Python 2和3上进行编译。
答案 0 :(得分:3)
但是这在Python 2上工作正常时给出了错误Python 3:
ValueError: '_fields' in __slots__ conflicts with class variable
。
虽然你没有像创建Py3k那样在类创建/编译时在Python2中收到错误,但如果你试图实际设置_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
>>>
另外,请参阅第四个note in the slots documentation:
通过为每个变量名创建描述符(实现描述符),在类级别实现
__slots__
。因此,类属性不能用于为__slots__
定义的实例变量设置默认值;否则,class属性将覆盖描述符赋值。
你的修改:
我将代码更改为
class B(object): __slots__ = ('_fields') def __init__(self): _fields = set()
修改后的B类在_fields
内部__init__()
,而不是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()
进一步的实验:
在D类中,__slots__
只是__init()__
内的变量。它不是(特殊)类变量D.__slots__
;甚至是实例变量self.__slots__
。所以它有__dict__
。
A类没有,所以也有__dict__
。
C类正确__slots__
。
答案 1 :(得分:0)
我个人发现解决此问题的最简单方法:
class B(object):
__slots__ = ('_fields')
_fields: set()
# Overridden in the derived classes
def __init__(self, _fields=None):
# your code
答案 2 :(得分:0)
(假设Python3)
在__slots__
中不必提及类属性。换句话说,即使该类是从object
派生的,并且其名称未出现在该类的__slots__
中,也可以定义一个类属性。
在您的情况下,实现此目标的正确方法是:
class B(object):
__slots__ = ('x', 'y', 'z') # e.g. restrict instance attributes (if wanted)
_fields = set() # define the class attribute independently.