使用类变量作为实例变量?

时间:2012-06-17 12:41:30

标签: python class

我想要做的是声明类变量,但实际上将它们用作实例的变量。我有一个班级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__)。是否还有另一种更直接的方法来实现这一目标?

3 个答案:

答案 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__ - 由于某种原因不是一种选择。如果这种方式适合你,你应该更喜欢它。