如何使用 `__slots__` 初始化属性?
How to use `__slots__` with initialization of attributes?
我通读了 usage of slots 上的主要答案,它让我了解了如何以及在何处使用 __slots__
。
现在,我将代码从 Python 2 移植到 Python 3,类似于以下内容:
class B(object):
__slots__ = ('_fields')
_fields = set()
但这给出了错误 Python 3 而在 Python 2 上工作正常:
ValueError: '_fields' in __slots__ conflicts with class variable
.
我把代码改成
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
.
虽然您在 class creation/compile 时没有像在 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__
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()
修改后的 class B 在 __init__()
中有一个 _fields
,而不是 self._fields
所以它只是 init 中的一个局部变量,在 [= =56=]。将其更改为:
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 进一步实验:
在classD中,__slots__
只是在__init()__
内部的一个变量。这不是(特殊的)class 变量 D.__slots__
;甚至是实例变量 self.__slots__
。所以它有 __dict__
.
Class A有none,所以也有__dict__
.
Class C 有 __slots__
正确。
我个人找到的解决此问题的最简单方法:
class B(object):
__slots__ = ('_fields')
_fields: set()
# Overridden in the derived classes
def __init__(self, _fields=None):
# your code
(假设 Python3)
class 属性不必在 __slots__
中提及。换句话说,即使 class 派生自 object
且其名称未出现在 class' __slots__
中,也可以定义 class 属性。
在您的情况下实现此目的的正确方法是:
class B(object):
__slots__ = ('x', 'y', 'z') # e.g. restrict instance attributes (if wanted)
_fields = set() # define the class attribute independently.
我刚刚有一个(愚蠢的?)想法,我真的不确定这是否“有效”Python,但它似乎奏效了(很快在 Python 3.7.7 中测试):
class Slotted:
__slots__ = {}
def __new__(cls, *args, **kwargs):
inst = super().__new__(cls)
for key, value in inst.__slots__.items():
setattr(inst, key, value)
return inst
class Magic(Slotted):
__slots__ = {
"foo": True,
"bar": 17
}
请注意,我(错误地?)在这里使用 __slots__
词典!
magic = Magic()
print(f"magic.foo = {magic.foo}")
print(f"magic.bar = {magic.bar}")
magic.foo = True
magic.bar = 17
有谁知道这样做有什么缺点吗?
我通读了 usage of slots 上的主要答案,它让我了解了如何以及在何处使用 __slots__
。
现在,我将代码从 Python 2 移植到 Python 3,类似于以下内容:
class B(object):
__slots__ = ('_fields')
_fields = set()
但这给出了错误 Python 3 而在 Python 2 上工作正常:
ValueError: '_fields' in __slots__ conflicts with class variable
.
我把代码改成
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
.
虽然您在 class creation/compile 时没有像在 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__
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()
修改后的 class B 在 __init__()
中有一个 _fields
,而不是 self._fields
所以它只是 init 中的一个局部变量,在 [= =56=]。将其更改为:
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 进一步实验:
在classD中,__slots__
只是在__init()__
内部的一个变量。这不是(特殊的)class 变量 D.__slots__
;甚至是实例变量 self.__slots__
。所以它有 __dict__
.
Class A有none,所以也有__dict__
.
Class C 有 __slots__
正确。
我个人找到的解决此问题的最简单方法:
class B(object):
__slots__ = ('_fields')
_fields: set()
# Overridden in the derived classes
def __init__(self, _fields=None):
# your code
(假设 Python3)
class 属性不必在 __slots__
中提及。换句话说,即使 class 派生自 object
且其名称未出现在 class' __slots__
中,也可以定义 class 属性。
在您的情况下实现此目的的正确方法是:
class B(object):
__slots__ = ('x', 'y', 'z') # e.g. restrict instance attributes (if wanted)
_fields = set() # define the class attribute independently.
我刚刚有一个(愚蠢的?)想法,我真的不确定这是否“有效”Python,但它似乎奏效了(很快在 Python 3.7.7 中测试):
class Slotted:
__slots__ = {}
def __new__(cls, *args, **kwargs):
inst = super().__new__(cls)
for key, value in inst.__slots__.items():
setattr(inst, key, value)
return inst
class Magic(Slotted):
__slots__ = {
"foo": True,
"bar": 17
}
请注意,我(错误地?)在这里使用 __slots__
词典!
magic = Magic()
print(f"magic.foo = {magic.foo}")
print(f"magic.bar = {magic.bar}")
magic.foo = True
magic.bar = 17
有谁知道这样做有什么缺点吗?