如何动态地向类添加属性? [英] How to add property to a class dynamically?

查看:56
本文介绍了如何动态地向类添加属性?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

目标是创建一个行为类似于数据库结果集的模拟类.

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

<预><代码>>>>虚拟文件100

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

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.b4

property 实际上是一个叫做 描述符.它是一个对象,它为给定的属性提供自定义处理,在给定的类上.有点像从 __getattribute__ 中分解出巨大的 if 树的方法.

当我在上面的例子中请求 foo.b 时,Python 看到定义在类上的 b 实现了描述符协议——这只是意味着它是一个带有 __get____set____delete__ 方法的对象.描述符声称负责处理该属性,因此 Python 调用 Foo.b.__get__(foo, Foo),并将返回值作为属性值传回给您.对于 property,这些方法中的每一个都只调用您传递给的 fgetfsetfdelproperty 构造函数.

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

<预><代码>>>>类 Foo(对象):...定义栏(自我):... 经过...>>>Foo().bar<在0x7f2a439d5dd0处<__main__.Foo对象的绑定方法Foo.bar>>>>>Foo().bar.__get__<位于 0x7f2a43a8a5a0 处的 instancemethod 对象的方法包装器__get__">

谦虚方法只是另一种描述符.它的 __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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆