Ant*_*ong 193 python monkeypatching runtime properties
目标是创建一个类似于db结果集的模拟类.
因此,例如,如果数据库查询使用dict表达式返回{'ab':100, 'cd':200},那么我想看到:
>>> dummy.ab
100
Run Code Online (Sandbox Code Playgroud)
起初我想也许我可以这样做:
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
Run Code Online (Sandbox Code Playgroud)
但是c.ab返回一个属性对象.
更换setattr线路k = property(lambda x: vs[i])完全没用.
那么在运行时创建实例属性的正确方法是什么?
Eev*_*vee 309
我想我应该扩大这个答案,现在我已经老了,更聪明,知道发生了什么.迟到总比不到好.
您可以动态地向类添加属性.但这就是问题:你必须把它添加到课堂上.
>>> class Foo(object):
... pass
...
>>> foo = Foo()
>>> foo.a = 3
>>> Foo.b = property(lambda self: self.a + 1)
>>> foo.b
4
Run Code Online (Sandbox Code Playgroud)
A property实际上是一个称为描述符的简单实现.它是一个为给定类提供给定属性的自定义处理的对象.有点像一种将if大树考虑在内的方法__getattribute__.
当我要求foo.b在上面的例子中,Python看到的是,b在该类中定义的实现描述符协议哪位只是意味着它是一个对象__get__,__set__或__delete__方法.描述符声称负责处理该属性,因此Python调用Foo.b.__get__(foo, Foo),并且返回值作为属性的值传递给您.在的情况下property,每一种方法只是调用fget,fset或fdel你传递给property构造函数.
描述符实际上是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>
Run Code Online (Sandbox Code Playgroud)
简陋的方法只是另一种描述符.它__get__的调用实例作为第一个参数固定物; 实际上,它这样做:
def __get__(self, instance, owner):
return functools.partial(self.function, instance)
Run Code Online (Sandbox Code Playgroud)
无论如何,我怀疑这就是为什么描述符只适用于类:它们是首先为类提供动力的东西的形式化.它们甚至是规则的例外:您显然可以将描述符分配给类,而类本身就是实例type!事实上,试图读取Foo.b静止的电话property.__get__; 当作为类属性访问时,描述符返回自己只是惯用的.
我认为几乎所有Python的OO系统都可以用Python表示,这很酷.:)
哦,如果你有兴趣的话,我前段时间写了一篇关于描述符的罗嗦博客文章.
bob*_*nce 56
目标是创建一个类似于db结果集的模拟类.
那么你想要的是一个字典,你可以拼写一个['b']作为ab?
这很简单:
class atdict(dict):
__getattr__= dict.__getitem__
__setattr__= dict.__setitem__
__delattr__= dict.__delitem__
Run Code Online (Sandbox Code Playgroud)
Eev*_*vee 34
看来你可以用a更简单地解决这个问题namedtuple,因为你提前了解了整个字段列表.
from collections import namedtuple
Foo = namedtuple('Foo', ['bar', 'quux'])
foo = Foo(bar=13, quux=74)
print foo.bar, foo.quux
foo2 = Foo() # error
Run Code Online (Sandbox Code Playgroud)
如果您绝对需要编写自己的setter,则必须在类级别进行元编程; property()不适用于实例.
Rya*_*yan 31
您不需要使用属性.只需覆盖__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!")
Run Code Online (Sandbox Code Playgroud)
田田.
>>> 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!
Run Code Online (Sandbox Code Playgroud)
这是一个解决方案:
定义类后,您只需执行以下操作即可动态添加属性:
setattr(SomeClass, 'propertyName', property(getter, setter))
Run Code Online (Sandbox Code Playgroud)
这是一个完整的示例,在 Python 3 中测试过:
setattr(SomeClass, 'propertyName', property(getter, setter))
Run Code Online (Sandbox Code Playgroud)
如何动态地向python类添加属性?
假设您有一个要添加属性的对象.通常,我想在需要开始管理具有下游使用的代码中的属性的访问时使用属性,以便我可以维护一致的API.现在我通常会将它们添加到定义对象的源代码中,但我们假设您没有该访问权限,或者您需要以编程方式真正动态地选择函数.
使用基于文档property的示例,让我们创建一个具有"隐藏"属性的对象类并创建它的实例:
class C(object):
'''basic class'''
_x = None
o = C()
Run Code Online (Sandbox Code Playgroud)
在Python中,我们期望有一种明显的做事方式.但是,在这种情况下,我将展示两种方式:使用装饰符号,而不使用.首先,没有装饰符号.这对于getter,setter或deleters的动态分配可能更有用.
让我们为我们的班级创建一些:
def getx(self):
return self._x
def setx(self, value):
self._x = value
def delx(self):
del self._x
Run Code Online (Sandbox Code Playgroud)
现在我们将这些分配给该物业.请注意,我们可以在这里以编程方式选择我们的函数,回答动态问题:
C.x = property(getx, setx, delx, "I'm the 'x' property.")
Run Code Online (Sandbox Code Playgroud)
用法:
>>> o.x = 'foo'
>>> o.x
'foo'
>>> del o.x
>>> print(o.x)
None
>>> help(C.x)
Help on property:
I'm the 'x' property.
Run Code Online (Sandbox Code Playgroud)
我们可以像上面用装饰符号那样做,但在这种情况下,我们必须将方法命名为同名(我建议保持它与属性相同),所以编程分配不是那么简单它使用上面的方法:
@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
Run Code Online (Sandbox Code Playgroud)
并将属性对象及其预配置的setter和deleters分配给该类:
C.x = x
Run Code Online (Sandbox Code Playgroud)
用法:
>>> 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
Run Code Online (Sandbox Code Playgroud)
对于那些来自搜索引擎的人,这里是我在谈论动态属性时要寻找的两件事:
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'
Run Code Online (Sandbox Code Playgroud)
__dict__如果您想放置动态创建的属性,这很好。__getattr__只在需要值时才做一些事情是好的,比如查询数据库。set/get 组合可以很好地简化对存储在类中的数据的访问(如上面的示例)。
如果您只需要一个动态属性,请查看property()内置函数。
不确定我是否完全理解这个问题,但是您可以在运行时使用类的内置修改实例属性__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
Run Code Online (Sandbox Code Playgroud)
我在这个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)
>>>
Run Code Online (Sandbox Code Playgroud)
您可以使用此变体来创建默认值,这是您的目标(在该问题中也有一个答案可以解决此问题).
小智 5
您可以使用以下代码使用字典对象更新类属性:
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
Run Code Online (Sandbox Code Playgroud)