我想要做的是声明类变量,但实际上将它们用作实例的变量。我有一个班级Field
和一个班级Thing
,如下所示:
class Field(object):
def __set__(self, instance, value):
for key, v in vars(instance.__class__).items():
if v is self:
instance.__dict__.update({key: value})
def __get__(self, instance, owner):
for key, v in vars(instance.__class__).items():
if v is self:
try:
return instance.__dict__[key]
except:
return None
class Thing(object):
foo = Field()
因此,当我实例化一个东西并设置属性 foo 时,它将被添加到实例,而不是类,类变量永远不会被重新设置。
new = Thing()
new.foo = 'bar'
# (foo : 'bar') is stored in new.__dict__
到目前为止这是有效的,但上面的Field代码相当尴尬。它也在类props中查找Field对象实例,否则似乎无法知道foo
和__set__
中属性的名称(__get__
)。是否还有另一种更直接的方法来实现这一目标?
答案 0 :(得分:2)
重读你的问题并意识到我错了:
您不需要覆盖默认的python行为来执行此操作。例如,您可以执行以下操作:
class Thing(object):
foo = 5
>>> r = Thing()
>>> r.foo = 10
>>> s = Thing()
>>> print Thing.foo
5
>>> print r.foo
10
>>> print s.foo
5
如果您希望特定变量的默认值为“None”,则只需将类范围值设置为None即可。也就是说,你必须专门为每个变量声明它。
答案 1 :(得分:2)
Field的每个实例(实际上)都有一个名称。它的名称是在Thing
中引用它的属性名称(或键)。您无需动态查找密钥,而是可以在Field
中设置class属性时使用名称实例化 Thing
:
class Field(object):
def __init__(self, name):
self.name = name
def __set__(self, instance, value):
instance.__dict__.update({self.name: value})
def __get__(self, instance, owner):
if instance is None:
return self
try:
return instance.__dict__[self.name]
except KeyError:
return None
def make_field(*args):
def wrapper(cls):
for arg in args:
setattr(cls, arg, Field(arg))
return cls
return wrapper
@make_field('foo')
class Thing(object):
pass
它可以像这样使用:
new = Thing()
在设置new.foo
之前,new.foo
会返回无:
print(new.foo)
# None
设置new.foo
后,'foo'
是new
的实例属性:
new.foo = 'bar'
print(new.__dict__)
# {'foo': 'bar'}
您可以使用Field
:
Thing.foo
实例本身)
print(Thing.foo)
# <__main__.Field object at 0xb76cedec>
PS。我假设你有充分的理由
class Thing(object):
foo = None
不够。
答案 2 :(得分:1)
最简单的方法是调用属性而不是描述符变量的名称 - 最好从_
开始,以表示其实现细节。这样,你最终得到:
def __set__(self, instance, value):
instance._foo = value
def __get__(self, instance, owner):
return getattr(instance, '_foo', None)
这样做的唯一缺点是您无法从用于描述符的密钥的名称中确定密钥的名称。如果与循环相比增加耦合不是问题,则可以使用property:
class Thing:
@property
def foo(self):
return getattr(self, '_foo', None)
@foo.setter
def foo(self, value):
self._foo = value
否则,您可以将变量的名称传递给描述符的__init__
,以便您拥有:
class Thing:
foo = Field('_foo')
当然,所有这些都假设最简单和最恐怖的方式 - 使用您在Thing().foo
中设置为None
的实际变量Thing.__init__
- 由于某种原因不是一种选择。如果这种方式适合你,你应该更喜欢它。