如何动态地向类添加属性? [英] How to add property to a class dynamically?
问题描述
目标是创建一个行为类似于数据库结果集的模拟类.
例如,如果一个数据库查询返回,使用一个dict表达式,{'ab':100, 'cd':200}
,那么我想看到:
起初我想也许我可以这样做:
ks = ['ab', 'cd']vs = [12, 34]C类(字典):def __init__(self, ks, vs):对于 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)提出它只是准备好了"如果 __name__ == "__main__":c = C(ks, vs)打印 c.ab
但是 c.ab
返回一个属性对象.
用 k = property(lambda x: vs[i])
替换 setattr
行根本没有用.
那么在运行时创建实例属性的正确方法是什么?
附言我知道 __getattribute__代码>使用的方法?
我想我应该扩展这个答案,现在我年纪大了,更聪明了,知道发生了什么.迟到总比不到好.
您可以向类动态添加属性.但这就是问题所在:您必须将其添加到类中.
<预><代码>>>>类 Foo(对象):... 经过...>>>foo = foo()>>>foo.a = 3>>>Foo.b = property(lambda self: self.a + 1)>>>foo.b4property
实际上是一个叫做 描述符.它是一个对象,它为给定的属性提供自定义处理,在给定的类上.有点像从 __getattribute__
中分解出巨大的 if
树的方法.
当我在上面的例子中请求 foo.b
时,Python 看到定义在类上的 b
实现了描述符协议——这只是意味着它是一个带有 __get__
、__set__
或 __delete__
方法的对象.描述符声称负责处理该属性,因此 Python 调用 Foo.b.__get__(foo, Foo)
,并将返回值作为属性值传回给您.对于 property
,这些方法中的每一个都只调用您传递给的 fget
、fset
或 fdel
property
构造函数.
描述符实际上是 Python 公开其整个 OO 实现的管道的方式.事实上,还有一种比 property
更常见的描述符.
谦虚方法只是另一种描述符.它的 __get__
将调用实例作为第一个参数;实际上,它是这样做的:
def __get__(self, instance, owner):返回 functools.partial(self.function, instance)
无论如何,我怀疑这就是描述符仅适用于类的原因:它们首先是为类提供动力的东西的形式化.它们甚至是规则的例外:您显然可以将描述符分配给一个类,而类本身就是 type
的实例!其实,试图读取Foo.bar
还是调用了property.__get__
;当作为类属性访问时,描述符返回自身是惯用的.
我认为几乎所有 Python 的 OO 系统都可以用 Python 表达,这非常酷.:)
哦,我写了一篇关于描述符的冗长的博客文章如果你有兴趣,请稍后.
The goal is to create a mock class which behaves like a db resultset.
So for example, if a database query returns, using a dict expression, {'ab':100, 'cd':200}
, then I would like to see:
>>> dummy.ab
100
At first I thought maybe I could do it this way:
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
but c.ab
returns a property object instead.
Replacing the setattr
line with k = property(lambda x: vs[i])
is of no use at all.
So what is the right way to create an instance property at runtime?
P.S. I am aware of an alternative presented in How is the __getattribute__
method used?
I suppose I should expand this answer, now that I'm older and wiser and know what's going on. Better late than never.
You can add a property to a class dynamically. But that's the catch: you have to add it to the class.
>>> class Foo(object):
... pass
...
>>> foo = Foo()
>>> foo.a = 3
>>> Foo.b = property(lambda self: self.a + 1)
>>> foo.b
4
A property
is actually a simple implementation of a thing called a descriptor. It's an object that provides custom handling for a given attribute, on a given class. Kinda like a way to factor a huge if
tree out of __getattribute__
.
When I ask for foo.b
in the example above, Python sees that the b
defined on the class implements the descriptor protocol—which just means it's an object with a __get__
, __set__
, or __delete__
method. The descriptor claims responsibility for handling that attribute, so Python calls Foo.b.__get__(foo, Foo)
, and the return value is passed back to you as the value of the attribute. In the case of property
, each of these methods just calls the fget
, fset
, or fdel
you passed to the property
constructor.
Descriptors are really Python's way of exposing the plumbing of its entire OO implementation. In fact, there's another type of descriptor even more common than 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>
The humble method is just another kind of descriptor. Its __get__
tacks on the calling instance as the first argument; in effect, it does this:
def __get__(self, instance, owner):
return functools.partial(self.function, instance)
Anyway, I suspect this is why descriptors only work on classes: they're a formalization of the stuff that powers classes in the first place. They're even the exception to the rule: you can obviously assign descriptors to a class, and classes are themselves instances of type
! In fact, trying to read Foo.bar
still calls property.__get__
; it's just idiomatic for descriptors to return themselves when accessed as class attributes.
I think it's pretty cool that virtually all of Python's OO system can be expressed in Python. :)
Oh, and I wrote a wordy blog post about descriptors a while back if you're interested.
这篇关于如何动态地向类添加属性?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!