如何动态地向类添加属性?

2024-04-19 21:30:41 发布

您现在位置:Python中文网/ 问答频道 /正文

目标是创建一个模拟类,其行为类似于db resultset。

例如,如果数据库查询使用dict表达式{'ab':100, 'cd':200}返回,那么我希望看到:

>>> dummy.ab
100

起初我想也许我可以这样做:

ks = ['ab', 'cd']
vs = [12, 34]
class C(dict):
    def __init__(self, ks, vs):
        for i, k in enumerate(ks):
            self[k] = vs[i]
            setattr(self, k, property(lambda x: vs[i], self.fn_readyonly))

    def fn_readonly(self, v)
        raise "It is ready only"

if __name__ == "__main__":
    c = C(ks, vs)
    print c.ab

但是c.ab返回一个属性对象。

k = property(lambda x: vs[i])替换setattr行完全没有用。

那么,在运行时创建实例属性的正确方法是什么?

p.S.我知道在How is the ^{} method used?中有一个替代方案


Tags: lambdaself目标db属性abisdef
3条回答

The goal is to create a mock class which behaves like a db resultset.

所以你想要的是一本字典,你可以把a['b']拼成a.b?

很简单:

class atdict(dict):
    __getattr__= dict.__getitem__
    __setattr__= dict.__setitem__
    __delattr__= dict.__delitem__

我想我应该扩大这个答案,因为我年纪大了,更聪明了,知道发生了什么事。迟到总比不到好。

您可以动态地将属性添加到类中。但这就是问题所在:必须将其添加到中。

>>> class Foo(object):
...     pass
... 
>>> foo = Foo()
>>> foo.a = 3
>>> Foo.b = property(lambda self: self.a + 1)
>>> foo.b
4

一个property实际上是一个叫做descriptor的东西的简单实现。它是一个对象,在给定的类上为给定的属性提供自定义处理。有点像把一棵巨大的if树从__getattribute__中分离出来的方法。

当我在上面的示例中要求foo.b时,Python看到类上定义的b实现了描述符协议-这意味着它是一个具有__get____set____delete__方法的对象。描述符声明负责处理该属性,因此Python调用Foo.b.__get__(foo, Foo),返回值作为属性的值传回给您。在property的情况下,这些方法中的每一个只调用传递给property构造函数的fgetfsetfdel

描述符实际上是Python公开其整个OO实现管道的方式。实际上,还有一种类型的描述符比property更常见。

>>> class Foo(object):
...     def bar(self):
...         pass
... 
>>> Foo().bar
<bound method Foo.bar of <__main__.Foo object at 0x7f2a439d5dd0>>
>>> Foo().bar.__get__
<method-wrapper '__get__' of instancemethod object at 0x7f2a43a8a5a0>

简陋的方法只是另一种描述。它的__get__将调用实例作为第一个参数;实际上,它这样做:

def __get__(self, instance, owner):
    return functools.partial(self.function, instance)

无论如何,我怀疑这就是为什么描述符只对类起作用的原因:它们首先是为类提供动力的东西的形式化。它们甚至是规则的例外:显然可以将描述符分配给类,类本身就是type的实例!事实上,尝试读取Foo.b仍然调用property.__get__;描述符在作为类属性访问时返回它们自己只是一种习惯。

我认为几乎所有Python的OO系统都可以用Python来表示是很酷的。:)

哦,如果你感兴趣的话,我刚才写了一篇wordy blog post about descriptors

似乎您可以用^{}更简单地解决这个问题,因为您提前知道了整个字段列表。

from collections import namedtuple

Foo = namedtuple('Foo', ['bar', 'quux'])

foo = Foo(bar=13, quux=74)
print foo.bar, foo.quux

foo2 = Foo()  # error

如果您确实需要编写自己的setter,则必须在类级别执行元编程;property()不能在实例上工作。

相关问题 更多 >