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


Answers

目标是创建一个类似db结果集的模拟类。

所以你想要的是一个字典,你可以拼出一个['b']作为ab?

这很容易:

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

目标是创建一个类似db结果集的模拟类。

因此,例如,如果数据库查询返回,使用字典表达式{'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])替换setattrk = property(lambda x: vs[i])根本没用。

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

PS我知道在如何使用__getattribute__方法中提供了一个替代方案




您可以使用以下代码使用字典对象更新类属性:

class ExampleClass():
    def __init__(self, argv):
        for key, val in argv.items():
            self.__dict__[key] = val

if __name__ == '__main__':
    argv = {'intro': 'Hello World!'}
    instance = ExampleClass(argv)
    print instance.intro



实现的最好方法是定义__slots__ 。 这样你的实例就不能有新的属性。

ks = ['ab', 'cd']
vs = [12, 34]

class C(dict):
    __slots__ = []
    def __init__(self, ks, vs): self.update(zip(ks, vs))
    def __getattr__(self, key): return self[key]

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

打印12

    c.ab = 33

这给了: AttributeError: 'C' object has no attribute 'ab'




在这个文章中提出了一个类似的问题来创建一个创建简单类型的类工厂。 结果是这个答案有一个工厂的工作版本。 这是答案的一小部分:

def Struct(*args, **kwargs):
    def init(self, *iargs, **ikwargs):
        for k,v in kwargs.items():
            setattr(self, k, v)
        for i in range(len(iargs)):
            setattr(self, args[i], iargs[i])
        for k,v in ikwargs.items():
            setattr(self, k, v)

    name = kwargs.pop("name", "MyStruct")
    kwargs.update(dict((k, None) for k in args))
    return type(name, (object,), {'__init__': init, '__slots__': kwargs.keys()})

>>> Person = Struct('fname', 'age')
>>> person1 = Person('Kevin', 25)
>>> person2 = Person(age=42, fname='Terry')
>>> person1.age += 10
>>> person2.age -= 10
>>> person1.fname, person1.age, person2.fname, person2.age
('Kevin', 35, 'Terry', 32)
>>>

你可以用一些变化来创建默认值,这是你的目标(在这个问题中也有一个答案,它处理这个问题)。




只有动态附加属性的方法是使用新属性创建新类及其实例。

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



为了回答你的问题的主要观点,你需要一个字典中的只读属性作为一个不可变的数据源:

目标是创建一个类似db结果集的模拟类。

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

>>> dummy.ab
100

我将演示如何使用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




你不需要为此使用一个属性。 只需重写__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!



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

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

创建一个班级

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

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

o = C()

在Python中,我们希望有一种明显的做事方式。 但是,在这种情况下,我将展示两种方式:使用装饰符号和不使用。 首先,没有装饰符号。 这对于获取者,设置者或删除者的动态分配可能更有用。

动态(又名猴子修补)

让我们为我们的班级创建一些:

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



Links