首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >如何为类动态添加属性?

如何为类动态添加属性?
EN

Stack Overflow用户
提问于 2009-08-25 01:53:33
回答 20查看 316.8K关注 0票数 269

目标是创建一个mock类,它的行为类似于db结果集。

例如,如果数据库查询使用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行根本没有任何用处。

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

附注:我知道在 中有一种替代方案

EN

回答 20

Stack Overflow用户

回答已采纳

发布于 2009-08-31 01:30:06

我想我应该扩展这个答案,现在我更老了,更聪明了,并且知道发生了什么。晚做总比不做强。

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

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

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

当我在上面的示例中请求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.bar仍然会调用property.__get__;当作为类属性访问时,描述符会返回自身,这是惯用的做法。

我认为几乎所有的Python面向对象系统都可以用Python表达,这是非常酷的。:)

哦,如果你感兴趣的话,我前段时间写了一个wordy blog post about descriptors

票数 403
EN

Stack Overflow用户

发布于 2009-08-25 02:41:47

为此,您不需要使用属性。只需覆盖__setattr__,使其成为只读。

class C(object):
    def __init__(self, keys, values):
        for (key, value) in zip(keys, values):
            self.__dict__[key] = value

    def __setattr__(self, name, value):
        raise Exception("It is read only!")

塔达。

>>> c = C('abc', [1,2,3])
>>> c.a
1
>>> c.b
2
>>> c.c
3
>>> c.d
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'C' object has no attribute 'd'
>>> c.d = 42
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 6, in __setattr__
Exception: It is read only!
>>> c.a = 'blah'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 6, in __setattr__
Exception: It is read only!
票数 41
EN

Stack Overflow用户

发布于 2009-08-25 02:28:06

看起来使用namedtuple可以更简单地解决这个问题,因为您提前知道了整个字段列表。

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()不能在实例上工作。

票数 40
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/1325673

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档