目标是创建一个行为类似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

但是cab返回一个属性对象。

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

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

附注:我知道在如何使用__getattribute__方法中提出了一个替代方案?


当前回答

这只是另一个如何达到预期效果的例子

class Foo(object):

    _bar = None

    @property
    def bar(self):
        return self._bar

    @bar.setter
    def bar(self, value):
        self._bar = value

    def __init__(self, dyn_property_name):
        setattr(Foo, dyn_property_name, Foo.bar)

现在我们可以这样做:

>>> foo = Foo('baz')
>>> foo.baz = 5
>>> foo.bar
5
>>> foo.baz
5

其他回答

动态附加属性的唯一方法是用新属性创建一个新类及其实例。

class Holder: p = property(lambda x: vs[i], self.fn_readonly)
setattr(self, k, Holder().p)

如何动态地将属性添加到python类?

假设您有一个希望向其添加属性的对象。通常,当我需要开始管理对具有下游用途的代码中的属性的访问时,我希望使用属性,以便能够维护一致的API。现在,我通常会将它们添加到定义对象的源代码中,但让我们假设您没有这种访问权限,或者您需要真正地以编程方式动态地选择函数。

创建类

使用一个基于属性文档的例子,让我们创建一个具有“hidden”属性的对象类,并创建它的一个实例:

class C(object):
    '''basic class'''
    _x = None

o = C()

在Python中,我们期望有一种明显的做事方式。但是,在本例中,我将展示两种方法:使用装饰符符号和不使用装饰符符号。首先,没有装饰符号。对于getter、setter或delete的动态赋值,这可能更有用。

动态(又名猴子修补)

让我们为我们的类创建一些:

def getx(self):
    return self._x

def setx(self, value):
    self._x = value

def delx(self):
    del self._x

现在我们把这些赋值给性质。注意,我们可以在这里以编程方式选择函数,回答动态问题:

C.x = property(getx, setx, delx, "I'm the 'x' property.")

和用法:

>>> o.x = 'foo'
>>> o.x
'foo'
>>> del o.x
>>> print(o.x)
None
>>> help(C.x)
Help on property:

    I'm the 'x' property.

修饰符

我们可以用上面的装饰符符号做同样的事情,但在这种情况下,我们必须将所有方法命名为相同的名称(我建议与属性保持相同),因此编程赋值并不像使用上述方法那样简单:

@property
def x(self):
    '''I'm the 'x' property.'''
    return self._x

@x.setter
def x(self, value):
    self._x = value

@x.deleter
def x(self):
    del self._x

并将属性对象及其配置的setter和deleters赋值给类:

C.x = x

和用法:

>>> help(C.x)
Help on property:

    I'm the 'x' property.

>>> o.x
>>> o.x = 'foo'
>>> o.x
'foo'
>>> del o.x
>>> print(o.x)
None

为了回答你的问题,你需要一个来自dict的只读属性作为不可变数据源:

目标是创建一个行为类似db结果集的模拟类。 例如,如果一个数据库查询返回一个dict表达式, {'ab':100, 'cd':200},那么我将看到 > > > dummy.ab One hundred.

我将演示如何使用collections模块中的namedtuple来实现这一点:

import collections

data = {'ab':100, 'cd':200}

def maketuple(d):
    '''given a dict, return a namedtuple'''
    Tup = collections.namedtuple('TupName', d.keys()) # iterkeys in Python2
    return Tup(**d)

dummy = maketuple(data)
dummy.ab

返回100

似乎您可以更简单地使用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()在实例上不起作用。

下面是以编程方式创建属性对象的简单示例。

#!/usr/bin/python3

class Counter:
    def __init__(self):
        cls = self.__class__
        self._count = 0
        cls.count = self.count_ref()

    def count_get(self):
        print(f'count_get: {self._count}')
        return self._count

    def count_set(self, value):
        self._count = value
        print(f'count_set: {self._count}')

    def count_del(self):
        print(f'count_del: {self._count}')

    def count_ref(self):
        cls = self.__class__
        return property(fget=cls.count_get, fset=cls.count_set, fdel=cls.count_del)

counter = Counter()

counter.count
for i in range(5):
    counter.count = i
del counter.count

'''
output
======
count_get: 0
count_set: 0
count_set: 1
count_set: 2
count_set: 3
count_set: 4
count_del: 4
'''