我希望能够在具有插槽优化的类中使用python描述符:
class C(object):
__slots__ = ['a']
a = MyDescriptor('a')
def __init__(self, val):
self.a = val
我遇到的问题是如何实现描述符类,以便能够在调用描述符对象的类实例中存储值。通常的解决方案看起来如下所示,但由于在C类中调用“slots”时不再定义“dict”,因此无法正常工作:
class MyDescriptor(object):
__slots__ = ['name']
def __init__(self, name_):
self.name = name_
def __get__(self, instance, owner):
if self.name not in instance.__dict__:
raise AttributeError, self.name
return instance.__dict__[self.name]
def __set__(self, instance, value):
instance.__dict__[self.name] = value
答案 0 :(得分:10)
不要声明与插槽相同的名称和实例方法。使用不同的名称,并将该插槽作为属性访问,而不是通过__dict__
。
class MyDescriptor(object):
__slots__ = ['name']
def __init__(self, name_):
self.name = name_
def __get__(self, instance, owner):
return getattr(instance, self.name)
def __set__(self, instance, value):
setattr(instance, self.name, value)
class C(object):
__slots__ = ['_a']
a = MyDescriptor('_a')
def __init__(self, val):
self.a = val
foo = C(1)
print foo.a
foo.a = 2
print foo.a
答案 1 :(得分:2)
虽然值得怀疑,但如果可以将第一个__slots__
放在子类中,则以下技巧将起作用。
class A( object ):
__slots__ = ( 'a', )
class B( A ):
__slots__ = ()
@property
def a( self ):
try:
return A.a.__get__( self )
except AttributeError:
return 'no a set'
@a.setter
def a( self, val ):
A.a.__set__( self, val )
(您可以使用自己的描述符而不是属性。)使用以下定义:
>>> b = B()
>>> b.a
'no a set'
>>> b.a = 'foo'
>>> b.a
'foo'
据我所知,__slots__
是用自己的描述符实现的,因此同一类__slots__
之后的另一个描述符只会覆盖。如果您想详细说明这种技术,可以在self.__class__.__mro__
中搜索候选描述符(或从您自己instance
中的__get__
开始)。
<强>后记强>
好吧......如果你真的想要使用一个类,你可以使用以下改编:
class C( object ):
__slots__ = ( 'c', )
class MyDescriptor( object ):
def __init__( self, slots_descriptor ):
self.slots_descriptor = slots_descriptor
def __get__( self, inst, owner = None ):
try:
return self.slots_descriptor.__get__( inst, owner )
except AttributeError:
return 'no c'
def __set__( self, inst, val ):
self.slots_descriptor.__set__( inst, val )
C.c = MyDescriptor( C.c )
如果你坚持不可摧毁,你可以在元类或类装饰器中进行赋值。
答案 2 :(得分:1)
@Glenn Maynard的答案很好。
但是我想指出OP问题中的一个问题(我不能在他的问题上添加评论,因为我还有足够的声誉):
以下测试在实例没有:=
变量时引发错误:
__dict__
所以,这是一个通用解决方案,它首先尝试访问 if self.name not in instance.__dict__:
变量(无论如何都是默认值),如果失败,请使用__dict__
和getattr
:
setattr
(仅当class WorksWithDictAndSlotsDescriptor:
def __init__(self, attr_name):
self.attr_name = attr_name
def __get__(self, instance, owner):
try:
return instance.__dict__[self.attr_name]
except AttributeError:
return getattr(instance, self.attr_name)
def __set__(self, instance, value):
try:
instance.__dict__[self.attr_name] = value
except AttributeError:
setattr(instance, self.attr_name, value)
与真实实例变量的名称不同时才有效,或者您在接受的答案中指定了attr_name
(如果同时有RecursionError
和__slots__
希望这有帮助。