如何动态地向类添加属性?

目标是创建一个行为类似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行根本没有用。

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

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

347647 次浏览

不确定我是否完全理解这个问题,但你可以在运行时使用类的内置__dict__修改实例属性:

class C(object):
def __init__(self, ks, vs):
self.__dict__ = dict(zip(ks, vs))




if __name__ == "__main__":
ks = ['ab', 'cd']
vs = [12, 34]
c = C(ks, vs)
print(c.ab) # 12

不能在运行时向实例添加新的property(),因为属性是数据描述符。相反,你必须动态地创建一个新类,或者重载__getattribute__以处理实例上的数据描述符。

似乎你可以用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()在实例上不起作用。

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

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

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

最好的方法是定义__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'

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

所以你想要的是一本可以把a['b']拼成a.b的字典?

这很简单:

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

这似乎是可行的(但见下文):

class data(dict,object):
def __init__(self,*args,**argd):
dict.__init__(self,*args,**argd)
self.__dict__.update(self)
def __setattr__(self,name,value):
raise AttributeError,"Attribute '%s' of '%s' object cannot be set"%(name,self.__class__.__name__)
def __delattr__(self,name):
raise AttributeError,"Attribute '%s' of '%s' object cannot be deleted"%(name,self.__class__.__name__)

如果您需要更复杂的行为,请随意编辑您的答案。

编辑

对于大型数据集,以下方法可能更节省内存:

class data(dict,object):
def __init__(self,*args,**argd):
dict.__init__(self,*args,**argd)
def __getattr__(self,name):
return self[name]
def __setattr__(self,name,value):
raise AttributeError,"Attribute '%s' of '%s' object cannot be set"%(name,self.__class__.__name__)
def __delattr__(self,name):
raise AttributeError,"Attribute '%s' of '%s' object cannot be deleted"%(name,self.__class__.__name__)
我问了一个类似的问题在这篇Stack Overflow文章中来创建一个创建简单类型的类工厂。结果是这个答案,它有类工厂的工作版本。 下面是答案的一小段:

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 Foo(object):
...     pass
...
>>> foo = Foo()
>>> foo.a = 3
>>> Foo.b = property(lambda self: self.a + 1)
>>> foo.b
4

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

当我在上面的例子中请求foo.b时,Python看到类中定义的b实现了__abc11 -这意味着它是一个具有__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的OO系统都可以用Python来表达,这非常酷。:)

哦,如果你感兴趣的话,我之前写了一个关于描述符的冗长博文

我最近遇到了一个类似的问题,我提出的解决方案使用__getattr____setattr__为我想要它处理的属性,其他一切都传递给原始。

class C(object):
def __init__(self, properties):
self.existing = "Still Here"
self.properties = properties


def __getattr__(self, name):
if "properties" in self.__dict__ and name in self.properties:
return self.properties[name] # Or call a function, etc
return self.__dict__[name]


def __setattr__(self, name, value):
if "properties" in self.__dict__ and name in self.properties:
self.properties[name] = value
else:
self.__dict__[name] = value


if __name__ == "__main__":
my_properties = {'a':1, 'b':2, 'c':3}
c = C(my_properties)
assert c.a == 1
assert c.existing == "Still Here"
c.b = 10
assert c.properties['b'] == 10

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

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

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

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

创建类

使用一个基于property的文档的例子,让我们创建一个带有&;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
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

对于那些来自搜索引擎的,这里是我在谈论动态属性时寻找的两件事:

class Foo:
def __init__(self):
# we can dynamically have access to the properties dict using __dict__
self.__dict__['foo'] = 'bar'


assert Foo().foo == 'bar'




# or we can use __getattr__ and __setattr__ to execute code on set/get
class Bar:
def __init__(self):
self._data = {}
def __getattr__(self, key):
return self._data[key]
def __setattr__(self, key, value):
self._data[key] = value


bar = Bar()
bar.foo = 'bar'
assert bar.foo == 'bar'

如果你想放置动态创建的属性,__dict__是很好的。__getattr__只适合在需要值时做一些事情,比如查询数据库。set/get组合有助于简化对存储在类中的数据的访问(如上面的示例)。

如果你只想要一个动态属性,可以看看财产()内置函数。

class atdict(dict):
def __init__(self, value, **kwargs):
super().__init__(**kwargs)
self.__dict = value


def __getattr__(self, name):
for key in self.__dict:
if type(self.__dict[key]) is list:
for idx, item in enumerate(self.__dict[key]):
if type(item) is dict:
self.__dict[key][idx] = atdict(item)
if type(self.__dict[key]) is dict:
self.__dict[key] = atdict(self.__dict[key])
return self.__dict[name]






d1 = atdict({'a' : {'b': [{'c': 1}, 2]}})


print(d1.a.b[0].c)

输出为:

>> 1

你可以使用下面的代码来使用字典对象更新类属性:

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

很多提供的答案需要这么多行每个属性,即/和/或-我认为一个丑陋或乏味的实现,因为多个属性需要重复,等等。我更喜欢把事情简化,直到它们不能再简化,或者直到这样做没有多大意义为止。

简而言之:在完成的作品中,如果我重复2行代码,我通常会将其转换为单行helper函数,等等……我简化了数学或奇怪的参数,如(start_x, start_y, end_x, end_y)到(x, y, w, h)即x, y, x + w, y + h(有时需要min / max或如果w / h为负,实现不喜欢它,我将从x / y和abs w / h等中减去。).

重写内部getter / setters是一个不错的方法,但问题是你需要对每个类都这样做,或者将类父化到那个基…这对我来说并不适用,因为我更喜欢自由地选择继承的子/父节点,子节点等。

我已经创建了一个解决方案,回答了这个问题,而不使用Dict数据类型来提供数据,因为我发现输入数据很乏味,等等…

我的解决方案需要你在你的类上面添加2行额外的代码,为你想要添加属性的类创建一个基类,然后每一行,你可以选择添加回调来控制数据,当数据发生变化时通知你,限制可以基于值和/或数据类型设置的数据,等等。

您还可以选择使用_object。x, _object。X = value, _object。GetX(), _object。SetX(value),它们被等效地处理。

此外,值是唯一分配给类实例的非静态数据,但实际的属性是分配给类的,这意味着你不想重复的东西,不需要重复……你可以指定一个默认值,这样getter每次都不需要它,尽管有一个选项可以覆盖默认的默认值,还有另一个选项,让getter通过覆盖默认返回返回原始存储值(注意:这个方法意味着原始值只在赋值时赋值,否则为None -当值为Reset时,则赋值为None,等等)。

还有很多helper函数——第一个属性添加了2个左右的helper到类中,用于引用实例值……它们是ResetAccessors(_key, ..)重复的varargs(都可以使用第一个命名的args重复)和SetAccessors(_key, _value),可以选择将更多的accessors添加到主类中以提高效率-计划的是:一种将访问器分组在一起的方法,因此如果你倾向于一次重置一些,每次,你可以将它们分配到一个组并重置组,而不是每次重复命名的键,以及更多。

实例/原始存储值存储在类。,即__类。引用访问器类,该类保存属性的静态变量/值/函数。_class。属性本身,在设置/获取等过程中通过实例类访问时被调用。

Accessor _class。__指向类,但因为它是内部的,所以需要在类中赋值,这就是为什么我选择使用__Name = AccessorFunc(…)要为它赋值,每个属性一行,并使用许多可选参数(使用键控可变参数,因为它们更容易识别和维护)…

我还创建了许多函数,如前所述,其中一些函数使用访问器函数信息,所以它不需要被调用(因为它在此刻有点不方便-现在你需要使用_class。。FunctionName(_class_instance, args) -我使用stack / trace来获取实例引用,通过添加运行这个比特马拉松的函数来获取值,或者通过向对象添加访问器并使用self(命名此为指出它们是用于实例的,并保留对self的访问权,AccessorFunc类引用,以及函数定义中的其他信息)。

它还没有完全完成,但它是一个很好的立足点。注意:如果你不使用__Name = AccessorFunc(…)要创建属性,你将无法访问__键,即使我在init函数中定义了它。如果你这样做了,那么就没有问题。

另外:注意Name和Key是不同的…名称为“正式”,用于函数名称创建,键用于数据存储和访问。ie _class。其中小写x是键,名称将是大写x,因此GetX()是函数而不是GetX(),这看起来有点奇怪。这允许self。x工作和看起来合适,但也允许GetX()和看起来合适。

我有一个例子类设置键/名称相同,不同的显示。为了输出数据而创建了许多helper函数(注意:不是所有这些都是完整的),所以你可以看到正在发生什么。

使用键:x,名称:x的当前函数列表输出如下:

这绝不是一个全面的列表-有一些在发布时还没有在这个列表上…

_instance.SetAccessors( _key, _value [ , _key, _value ] .. )                   Instance Class Helper Function: Allows assigning many keys / values on a single line - useful for initial setup, or to minimize lines.    In short: Calls this.Set<Name>( _value ) for each _key / _value pairing.
_instance.ResetAccessors( _key [ , _key ] .. )                                 Instance Class Helper Function: Allows resetting many key stored values to None on a single line.                                           In short: Calls this.Reset<Name>() for each name provided.




Note: Functions below may list self.Get / Set / Name( _args ) - self is meant as the class instance reference in the cases below - coded as this in AccessorFuncBase Class.


this.GetX( _default_override = None, _ignore_defaults = False )                 GET:            Returns    IF ISSET: STORED_VALUE .. IF IGNORE_DEFAULTS: None  .. IF PROVIDED: DEFAULT_OVERRIDE ELSE: DEFAULT_VALUE       100
this.GetXRaw( )                                                                 RAW:            Returns    STORED_VALUE                                                                                                     100
this.IsXSet( )                                                                  ISSET:          Returns    ( STORED_VALUE != None )                                                                                         True


this.GetXToString( )                                                            GETSTR:         Returns    str( GET )                                                                                                       100
this.GetXLen( _default_override = None, _ignore_defaults = False )              LEN:            Returns    len( GET )                                                                                                       3
this.GetXLenToString( _default_override = None, _ignore_defaults = False )      LENSTR:         Returns    str( len( GET ) )                                                                                                3
this.GetXDefaultValue( )                                                        DEFAULT:        Returns    DEFAULT_VALUE                                                                                                    1111


this.GetXAccessor( )                                                            ACCESSOR:       Returns    ACCESSOR_REF ( self.__<key> )                                                                                    [ AccessorFuncBase ] Key: x : Class ID: 2231452344344 : self ID: 2231448283848        Default: 1111       Allowed Types: {"<class 'int'>": "<class 'type'>", "<class 'float'>": "<class 'type'>"}     Allowed Values: None
this.GetXAllowedTypes( )                                                        ALLOWED_TYPES:  Returns    Allowed Data-Types                                                                                               {"<class 'int'>": "<class 'type'>", "<class 'float'>": "<class 'type'>"}
this.GetXAllowedValues( )                                                       ALLOWED_VALUES: Returns    Allowed Values                                                                                                   None


this.GetXHelpers( )                                                             HELPERS:        Returns    Helper Functions String List - ie what you're reading now...                                                     THESE ROWS OF TEXT
this.GetXKeyOutput( )                                                           Returns information about this Name / Key                                                                                                   ROWS OF TEXT
this.GetXGetterOutput( )                                                        Returns information about this Name / Key                                                                                                   ROWS OF TEXT


this.SetX( _value )                                                             SET:            STORED_VALUE Setter - ie Redirect to __<Key>.Set                                                                            N / A
this.ResetX( )                                                                  RESET:          Resets STORED_VALUE to None                                                                                                 N / A


this.HasXGetterPrefix( )                                                        Returns Whether or Not this key has a Getter Prefix...                                                                                      True
this.GetXGetterPrefix( )                                                        Returns Getter Prefix...                                                                                                                    Get


this.GetXName( )                                                                Returns Accessor Name - Typically Formal / Title-Case                                                                                       X
this.GetXKey( )                                                                 Returns Accessor Property Key - Typically Lower-Case                                                                                        x
this.GetXAccessorKey( )                                                         Returns Accessor Key - This is to access internal functions, and static data...                                                             __x
this.GetXDataKey( )                                                             Returns Accessor Data-Storage Key - This is the location where the class instance value is stored..                                         _x

输出的一些数据是:

这是一个全新的类创建使用Demo类没有任何数据分配除了名称(所以它可以输出),这是_foo,变量名我使用…

_foo         --- MyClass: ---- id( this.__class__ ): 2231452349064 :::: id( this ): 2231448475016


Key       Getter Value        | Raw Key   Raw / Stored Value       | Get Default Value             Default Value            | Get Allowed Types             Allowed Types                                                              | Get Allowed Values            Allowed Values                                                                                                                                                                                                                   |


Name:     _foo                | _Name:    _foo                     | __Name.DefaultValue( ):       AccessorFuncDemoClass    | __Name.GetAllowedTypes( )     <class 'str'>                                                              | __Name.GetAllowedValues( )    Saved Value Restrictions Levied by Data-Type                                                                                                                                                                                     |
x:        1111                | _x:       None                     | __x.DefaultValue( ):          1111                     | __x.GetAllowedTypes( )        (<class 'int'>, <class 'float'>)                                           | __x.GetAllowedValues( )       Saved Value Restrictions Levied by Data-Type                                                                                                                                                                                     |
y:        2222                | _y:       None                     | __y.DefaultValue( ):          2222                     | __y.GetAllowedTypes( )        (<class 'int'>, <class 'float'>)                                           | __y.GetAllowedValues( )       Saved Value Restrictions Levied by Data-Type                                                                                                                                                                                     |
z:        3333                | _z:       None                     | __z.DefaultValue( ):          3333                     | __z.GetAllowedTypes( )        (<class 'int'>, <class 'float'>)                                           | __z.GetAllowedValues( )       Saved Value Restrictions Levied by Data-Type                                                                                                                                                                                     |
Blah:     <class 'int'>       | _Blah:    None                     | __Blah.DefaultValue( ):       <class 'int'>            | __Blah.GetAllowedTypes( )     <class 'str'>                                                              | __Blah.GetAllowedValues( )    Saved Value Restrictions Levied by Data-Type                                                                                                                                                                                     |
Width:    1                   | _Width:   None                     | __Width.DefaultValue( ):      1                        | __Width.GetAllowedTypes( )    (<class 'int'>, <class 'bool'>)                                            | __Width.GetAllowedValues( )   Saved Value Restrictions Levied by Data-Type                                                                                                                                                                                     |
Height:   0                   | _Height:  None                     | __Height.DefaultValue( ):     0                        | __Height.GetAllowedTypes( )   <class 'int'>                                                              | __Height.GetAllowedValues( )  (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)                                                                                                                                                                                                   |
Depth:    2                   | _Depth:   None                     | __Depth.DefaultValue( ):      2                        | __Depth.GetAllowedTypes( )    Saved Value Restricted to Authorized Values ONLY                           | __Depth.GetAllowedValues( )   (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)                                                                                                                                                                                                   |




this.IsNameSet( ):    True      this.GetName( ):     _foo                     this.GetNameRaw( ):    _foo                     this.GetNameDefaultValue( ):    AccessorFuncDemoClass    this.GetNameLen( ):    4    this.HasNameGetterPrefix( ):    <class 'str'>                                this.GetNameGetterPrefix( ):    None
this.IsXSet( ):       False     this.GetX( ):        1111                     this.GetXRaw( ):       None                     this.GetXDefaultValue( ):       1111                     this.GetXLen( ):       4    this.HasXGetterPrefix( ):       (<class 'int'>, <class 'float'>)             this.GetXGetterPrefix( ):       None
this.IsYSet( ):       False     this.GetY( ):        2222                     this.GetYRaw( ):       None                     this.GetYDefaultValue( ):       2222                     this.GetYLen( ):       4    this.HasYGetterPrefix( ):       (<class 'int'>, <class 'float'>)             this.GetYGetterPrefix( ):       None
this.IsZSet( ):       False     this.GetZ( ):        3333                     this.GetZRaw( ):       None                     this.GetZDefaultValue( ):       3333                     this.GetZLen( ):       4    this.HasZGetterPrefix( ):       (<class 'int'>, <class 'float'>)             this.GetZGetterPrefix( ):       None
this.IsBlahSet( ):    False     this.GetBlah( ):     <class 'int'>            this.GetBlahRaw( ):    None                     this.GetBlahDefaultValue( ):    <class 'int'>            this.GetBlahLen( ):    13   this.HasBlahGetterPrefix( ):    <class 'str'>                                this.GetBlahGetterPrefix( ):    None
this.IsWidthSet( ):   False     this.GetWidth( ):    1                        this.GetWidthRaw( ):   None                     this.GetWidthDefaultValue( ):   1                        this.GetWidthLen( ):   1    this.HasWidthGetterPrefix( ):   (<class 'int'>, <class 'bool'>)              this.GetWidthGetterPrefix( ):   None
this.IsDepthSet( ):   False     this.GetDepth( ):    2                        this.GetDepthRaw( ):   None                     this.GetDepthDefaultValue( ):   2                        this.GetDepthLen( ):   1    this.HasDepthGetterPrefix( ):   None                                         this.GetDepthGetterPrefix( ):   (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
this.IsHeightSet( ):  False     this.GetHeight( ):   0                        this.GetHeightRaw( ):  None                     this.GetHeightDefaultValue( ):  0                        this.GetHeightLen( ):  1    this.HasHeightGetterPrefix( ):  <class 'int'>                                this.GetHeightGetterPrefix( ):  (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)

这是在分配了所有_foo属性(除了名称)之后,以相同的顺序:'string ', 1.0, True, 9,10, False

this.IsNameSet( ):    True      this.GetName( ):     _foo                     this.GetNameRaw( ):    _foo                     this.GetNameDefaultValue( ):    AccessorFuncDemoClass    this.GetNameLen( ):    4    this.HasNameGetterPrefix( ):    <class 'str'>                                this.GetNameGetterPrefix( ):    None
this.IsXSet( ):       True      this.GetX( ):        10                       this.GetXRaw( ):       10                       this.GetXDefaultValue( ):       1111                     this.GetXLen( ):       2    this.HasXGetterPrefix( ):       (<class 'int'>, <class 'float'>)             this.GetXGetterPrefix( ):       None
this.IsYSet( ):       True      this.GetY( ):        10                       this.GetYRaw( ):       10                       this.GetYDefaultValue( ):       2222                     this.GetYLen( ):       2    this.HasYGetterPrefix( ):       (<class 'int'>, <class 'float'>)             this.GetYGetterPrefix( ):       None
this.IsZSet( ):       True      this.GetZ( ):        10                       this.GetZRaw( ):       10                       this.GetZDefaultValue( ):       3333                     this.GetZLen( ):       2    this.HasZGetterPrefix( ):       (<class 'int'>, <class 'float'>)             this.GetZGetterPrefix( ):       None
this.IsBlahSet( ):    True      this.GetBlah( ):     string Blah              this.GetBlahRaw( ):    string Blah              this.GetBlahDefaultValue( ):    <class 'int'>            this.GetBlahLen( ):    11   this.HasBlahGetterPrefix( ):    <class 'str'>                                this.GetBlahGetterPrefix( ):    None
this.IsWidthSet( ):   True      this.GetWidth( ):    False                    this.GetWidthRaw( ):   False                    this.GetWidthDefaultValue( ):   1                        this.GetWidthLen( ):   5    this.HasWidthGetterPrefix( ):   (<class 'int'>, <class 'bool'>)              this.GetWidthGetterPrefix( ):   None
this.IsDepthSet( ):   True      this.GetDepth( ):    9                        this.GetDepthRaw( ):   9                        this.GetDepthDefaultValue( ):   2                        this.GetDepthLen( ):   1    this.HasDepthGetterPrefix( ):   None                                         this.GetDepthGetterPrefix( ):   (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
this.IsHeightSet( ):  True      this.GetHeight( ):   9                        this.GetHeightRaw( ):  9                        this.GetHeightDefaultValue( ):  0                        this.GetHeightLen( ):  1    this.HasHeightGetterPrefix( ):  <class 'int'>                                this.GetHeightGetterPrefix( ):  (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)


_foo         --- MyClass: ---- id( this.__class__ ): 2231452349064 :::: id( this ): 2231448475016


Key       Getter Value        | Raw Key   Raw / Stored Value       | Get Default Value             Default Value            | Get Allowed Types             Allowed Types                                                              | Get Allowed Values            Allowed Values                                                                                                                                                                                                                   |


Name:     _foo                | _Name:    _foo                     | __Name.DefaultValue( ):       AccessorFuncDemoClass    | __Name.GetAllowedTypes( )     <class 'str'>                                                              | __Name.GetAllowedValues( )    Saved Value Restrictions Levied by Data-Type                                                                                                                                                                                     |
x:        10                  | _x:       10                       | __x.DefaultValue( ):          1111                     | __x.GetAllowedTypes( )        (<class 'int'>, <class 'float'>)                                           | __x.GetAllowedValues( )       Saved Value Restrictions Levied by Data-Type                                                                                                                                                                                     |
y:        10                  | _y:       10                       | __y.DefaultValue( ):          2222                     | __y.GetAllowedTypes( )        (<class 'int'>, <class 'float'>)                                           | __y.GetAllowedValues( )       Saved Value Restrictions Levied by Data-Type                                                                                                                                                                                     |
z:        10                  | _z:       10                       | __z.DefaultValue( ):          3333                     | __z.GetAllowedTypes( )        (<class 'int'>, <class 'float'>)                                           | __z.GetAllowedValues( )       Saved Value Restrictions Levied by Data-Type                                                                                                                                                                                     |
Blah:     string Blah         | _Blah:    string Blah              | __Blah.DefaultValue( ):       <class 'int'>            | __Blah.GetAllowedTypes( )     <class 'str'>                                                              | __Blah.GetAllowedValues( )    Saved Value Restrictions Levied by Data-Type                                                                                                                                                                                     |
Width:    False               | _Width:   False                    | __Width.DefaultValue( ):      1                        | __Width.GetAllowedTypes( )    (<class 'int'>, <class 'bool'>)                                            | __Width.GetAllowedValues( )   Saved Value Restrictions Levied by Data-Type                                                                                                                                                                                     |
Height:   9                   | _Height:  9                        | __Height.DefaultValue( ):     0                        | __Height.GetAllowedTypes( )   <class 'int'>                                                              | __Height.GetAllowedValues( )  (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)                                                                                                                                                                                                   |
Depth:    9                   | _Depth:   9                        | __Depth.DefaultValue( ):      2                        | __Depth.GetAllowedTypes( )    Saved Value Restricted to Authorized Values ONLY                           | __Depth.GetAllowedValues( )   (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)                                                                                                                                                                                                   |

注意,由于数据类型或值的限制,一些数据没有被分配——这是有意为之。setter禁止分配错误的数据类型或值,甚至禁止将其分配为默认值(除非覆盖默认值保护行为)

代码没有发布在这里,因为在示例和解释之后我没有空间了……也因为它会改变。

请注意:在这个发布的时候,文件是混乱的-这将会改变。但是,如果你在Sublime Text中运行它并编译它,或者从Python中运行它,它将编译并输出大量信息- AccessorDB部分还没有完成(将用于更新Print getter和GetKeyOutput helper函数,同时被更改为一个实例函数,可能放入一个单独的函数并重命名-寻找它..)

其次:并不是运行它所需要的所有东西——底部的许多注释是用于调试的更多信息——当你下载它时,它可能不存在。如果是,您应该能够取消注释并重新编译以获得更多信息。

我正在寻找一个工作区,需要MyClassBase:通过,MyClass(MyClassBase):…如果你知道一个解决方案,把它贴出来。

类中唯一需要的是__行——str是用于调试的,初始化也是——它们可以从演示类中删除,但你需要注释掉或删除下面的一些行(_foo / 2 / 3)..

顶部的String、Dict和Util类是我的Python库的一部分——它们不是完整的。我从图书馆复制了一些我需要的东西,并创建了一些新的东西。完整的代码将链接到完整的库,并将包括它以及提供更新的调用和删除代码(实际上,唯一剩下的代码将是演示类和打印语句- AccessorFunc系统将被移动到库)…

文件部分:

##
## MyClass Test AccessorFunc Implementation for Dynamic 1-line Parameters
##
class AccessorFuncDemoClassBase( ):
pass
class AccessorFuncDemoClass( AccessorFuncDemoClassBase ):
__Name      = AccessorFuncBase( parent = AccessorFuncDemoClassBase, name = 'Name',      default = 'AccessorFuncDemoClass',  allowed_types = ( TYPE_STRING ),                    allowed_values = VALUE_ANY,                 documentation = 'Name Docs',        getter_prefix = 'Get',  key = 'Name',       allow_erroneous_default = False,    options = { } )
__x         = AccessorFuncBase( parent = AccessorFuncDemoClassBase, name = 'X',         default = 1111,                     allowed_types = ( TYPE_INTEGER, TYPE_FLOAT ),       allowed_values = VALUE_ANY,                 documentation = 'X Docs',           getter_prefix = 'Get',  key = 'x',          allow_erroneous_default = False,    options = { } )
__Height    = AccessorFuncBase( parent = AccessorFuncDemoClassBase, name = 'Height',    default = 0,                        allowed_types = TYPE_INTEGER,                       allowed_values = VALUE_SINGLE_DIGITS,       documentation = 'Height Docs',      getter_prefix = 'Get',  key = 'Height',     allow_erroneous_default = False,    options = { } )

这种美感使得创建具有AccessorFuncs /回调/数据类型/值强制等动态添加属性的新类非常容易。

目前,该链接位于(该链接应反映文档的更改)。: https://www.dropbox.com/s/6gzi44i7dh58v61/dynamic_properties_accessorfuncs_and_more.py?dl=0

另外:如果你不使用Sublime Text,我推荐它优于notepad++, Atom, Visual Code和其他,因为适当的线程实现使它使用起来更快…我也在为它开发一个类似ide的代码映射系统-看看:https://bitbucket.org/Acecool/acecoolcodemappingsystem/src/master/(先在包管理器中添加Repo,然后安装插件-当版本1.0.0准备好了,我将把它添加到主插件列表中…)

我希望这个解决方案能有所帮助……而且,一如既往:

仅仅因为它有效,并不意味着它就是正确的——Josh 'Acecool' Moser

kjfletch扩展思想

# This is my humble contribution, extending the idea to serialize
# data from and to tuples, comparison operations and allowing functions
# as default values.


def Struct(*args, **kwargs):
FUNCTIONS = (types.BuiltinFunctionType, types.BuiltinMethodType, \
types.FunctionType, types.MethodType)
def init(self, *iargs, **ikwargs):
"""Asume that unamed args are placed in the same order than
astuple() yields (currently alphabetic order)
"""
kw = list(self.__slots__)


# set the unnamed args
for i in range(len(iargs)):
k = kw.pop(0)
setattr(self, k, iargs[i])


# set the named args
for k, v in ikwargs.items():
setattr(self, k, v)
kw.remove(k)


# set default values
for k in kw:
v = kwargs[k]
if isinstance(v, FUNCTIONS):
v = v()
setattr(self, k, v)


def astuple(self):
return tuple([getattr(self, k) for k in self.__slots__])


def __str__(self):
data = ['{}={}'.format(k, getattr(self, k)) for k in self.__slots__]
return '<{}: {}>'.format(self.__class__.__name__, ', '.join(data))


def __repr__(self):
return str(self)


def __eq__(self, other):
return self.astuple() == other.astuple()


name = kwargs.pop("__name__", "MyStruct")
slots = list(args)
slots.extend(kwargs.keys())
# set non-specific default values to None
kwargs.update(dict((k, None) for k in args))


return type(name, (object,), {
'__init__': init,
'__slots__': tuple(slots),
'astuple': astuple,
'__str__': __str__,
'__repr__': __repr__,
'__eq__': __eq__,
})




Event = Struct('user', 'cmd', \
'arg1', 'arg2',  \
date=time.time, \
__name__='Event')


aa = Event('pepe', 77)
print(aa)
raw = aa.astuple()


bb = Event(*raw)
print(bb)


if aa == bb:
print('Are equals')


cc = Event(cmd='foo')
print(cc)

输出:

<Event: user=pepe, cmd=77, arg1=None, arg2=None, date=1550051398.3651814>
<Event: user=pepe, cmd=77, arg1=None, arg2=None, date=1550051398.3651814>
Are equals
<Event: user=None, cmd=foo, arg1=None, arg2=None, date=1550051403.7938335>

虽然给出了很多答案,但我没有找到一个让我满意的。我找到了自己的解决方案,使property适用于动态情况。来源回答原来的问题:

#!/usr/local/bin/python3


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


class DP(dict):
def __init__(self):
super().__init__()
for k,v in INITS.items():
self[k] = v


def _dict_set(dp, key, value):
dp[key] = value


for item in INITS.keys():
setattr(
DP,
item,
lambda key: property(
lambda self: self[key], lambda self, value: _dict_set(self, key, value)
)(item)
)


a = DP()
print(a)  # {'ab': 100, 'cd': 200}
a.ab = 'ab100'
a.cd = False
print(a.ab, a.cd) # ab100 False

对我有用的是:

class C:
def __init__(self):
self._x=None


def g(self):
return self._x


def s(self, x):
self._x = x


def d(self):
del self._x


def s2(self,x):
self._x=x+x


x=property(g,s,d)




c = C()
c.x="a"
print(c.x)


C.x=property(C.g, C.s2)
C.x=C.x.deleter(C.d)
c2 = C()
c2.x="a"
print(c2.x)

输出

a
aa

这和OP想要的有点不同,但我绞尽脑汁,直到我得到了一个可行的解决方案,所以我把它放在这里给下一个男人/女孩

我需要一种方法来指定动态setter和getter。

class X:
def __init__(self, a=0, b=0, c=0):
self.a = a
self.b = b
self.c = c


@classmethod
def _make_properties(cls, field_name, inc):
_inc = inc


def _get_properties(self):
if not hasattr(self, '_%s_inc' % field_name):
setattr(self, '_%s_inc' % field_name, _inc)
inc = _inc
else:
inc = getattr(self, '_%s_inc' % field_name)


return getattr(self, field_name) + inc


def _set_properties(self, value):
setattr(self, '_%s_inc' % field_name, value)


return property(_get_properties, _set_properties)

我知道我的字段提前,所以我要创建我的属性。注意:你不能在每个实例中这样做,这些属性将存在于类中!!

for inc, field in enumerate(['a', 'b', 'c']):
setattr(X, '%s_summed' % field, X._make_properties(field, inc))

现在让我们来测试一下。

x = X()
assert x.a == 0
assert x.b == 0
assert x.c == 0


assert x.a_summed == 0  # enumerate() set inc to 0 + 0 = 0
assert x.b_summed == 1  # enumerate() set inc to 1 + 0 = 1
assert x.c_summed == 2  # enumerate() set inc to 2 + 0 = 2


# we set the variables to something
x.a = 1
x.b = 2
x.c = 3


assert x.a_summed == 1  # enumerate() set inc to 0 + 1 = 1
assert x.b_summed == 3  # enumerate() set inc to 1 + 2 = 3
assert x.c_summed == 5  # enumerate() set inc to 2 + 3 = 5


# we're changing the inc now
x.a_summed = 1
x.b_summed = 3
x.c_summed = 5


assert x.a_summed == 2  # we set inc to 1 + the property was 1 = 2
assert x.b_summed == 5  # we set inc to 3 + the property was 2 = 5
assert x.c_summed == 8  # we set inc to 5 + the property was 3 = 8

会让人困惑吗?是的,很抱歉我无法举出任何有意义的现实例子。另外,这也不适合轻松的人。

这里有一个解决方案:

  • 允许将属性名指定为字符串,所以它们可以来自一些外部数据源,而不是全部列在你的程序中。
  • 在定义类时添加属性,而不是每次创建对象时。

定义类之后,你只需动态地向它添加一个属性:

setattr(SomeClass, 'propertyName', property(getter, setter))

下面是一个完整的例子,用Python 3测试过:

#!/usr/bin/env python3


class Foo():
pass


def get_x(self):
return 3


def set_x(self, value):
print("set x on %s to %d" % (self, value))


setattr(Foo, 'x', property(get_x, set_x))


foo1 = Foo()
foo1.x = 12
print(foo1.x)

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

#!/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
'''

如果需求是基于某些实例属性动态生成属性,那么下面的代码可能会有用:

import random


class Foo:
def __init__(self, prop_names: List[str], should_property_be_zero: bool = False) -> None:
self.prop_names = prop_names
self.should_property_be_zero = should_property_be_zero
        

def create_properties(self):
for name in self.prop_names:
setattr(self.__class__, name, property(fget=lambda self: 0 if self.should_property_be_zero else random.randint(1, 100)))

需要注意的重点是使用setattr(self.__class__, name, ...)而不是setattr(self, name, ...)

使用的例子:

In [261]: prop_names = ['a', 'b']


In [262]: ff = Foo(prop_names=prop_names, should_property_be_zero=False)


In [263]: ff.create_properties()


In [264]: ff.a
Out[264]: 10


In [265]: ff.b
Out[265]: 37


In [266]: ft = Foo(prop_names=prop_names, should_property_be_zero=True)


In [267]: ft.create_properties()


In [268]: ft.a
Out[268]: 0


In [269]: ft.b
Out[269]: 0

设置该属性将如预期的那样引发AttributeError: can't set attribute:

In [270]: ff.a = 5
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-270-5f9cad5b617d> in <module>
----> 1 ff.a = 5


AttributeError: can't set attribute


In [271]: ft.a = 5
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-271-65e7b8e25b67> in <module>
----> 1 ft.a = 5


AttributeError: can't set attribute