我有其他人写的代码:
class MyClass(object):
def __init__(self, data):
self.data = data
@property
def attribute1(self):
return self.data.another_name1
@property
def attribute2(self):
return self.data.another_name2
我想在运行时自动创建相应的属性设置器,因此我不必修改其他人的代码。属性设置器应如下所示:
@attribute1.setter
def attribue1(self, val):
self.data.another_name1= val
@attribute2.setter
def attribue2(self, val):
self.data.another_name2= val
如何将这些setter方法动态添加到类中?
答案 0 :(得分:4)
您可以像这样编写自定义描述符:
from operator import attrgetter
class CustomProperty(object):
def __init__(self, attr):
self.attr = attr
def __get__(self, ins, type):
print 'inside __get__'
if ins is None:
return self
else:
return attrgetter(self.attr)(ins)
def __set__(self, ins, value):
print 'inside __set__'
head, tail = self.attr.rsplit('.', 1)
obj = attrgetter(head)(ins)
setattr(obj, tail, value)
class MyClass(object):
def __init__(self, data):
self.data = data
attribute1 = CustomProperty('data.another_name1')
attribute2 = CustomProperty('data.another_name2')
<强>演示:强>
>>> class Foo():
... pass
...
>>> bar = MyClass(Foo())
>>>
>>> bar.attribute1 = 10
inside __set__
>>> bar.attribute2 = 20
inside __set__
>>> bar.attribute1
inside __get__
10
>>> bar.attribute2
inside __get__
20
>>> bar.data.another_name1
10
>>> bar.data.another_name2
20
答案 1 :(得分:2)
这是问题的作者。我发现了一个非常简单的解决方案,但我不知道另一种方法。 (我顺便使用python 3.4。)
我将从遇到的问题开始。
首先,我想要完全覆盖财产,如下所示:
鉴于此课程
class A(object):
def __init__(self):
self._value = 42
@property
def value(self):
return self._value
你可以通过做这样的事情来完全写出财产:
a = A()
A.value = 31 # This just redirects A.value from the @property to the int 31
a.value # Returns 31
问题是这是在类级而不是在实例级别完成的,所以如果我创建一个新的A实例,那么会发生这种情况:
a2 = A()
a.value # Returns 31, because the class itself was modified in the previous code block.
我希望返回a2._value
,因为a2
是A()
的全新实例,因此不应受我对a
所做的影响。
对此的解决方案是使用新属性覆盖A.value
,而不是我想要将实例_value
分配给的任何内容。我了解到您可以创建一个新属性,使用特殊的getter
,setter
和deleter
方法从旧属性中实例化自己(请参阅here)。所以我可以覆盖A
的value属性并通过这样做为它设置一个setter:
def make_setter(name):
def value_setter(self, val):
setattr(self, name, val)
return value_setter
my_setter = make_setter('_value')
A.value = A.value.setter(my_setter) # This takes the property defined in the above class and overwrites the setter with my_setter
setattr(A, 'value', getattr(A, 'value').setter(my_setter)) # This does the same thing as the line above I think so you only need one of them
只要原始类在原始类的属性定义中具有非常简单的东西(在这种情况下它只是return self._value
),这一切都很好。然而,一旦你变得更加复杂,像我一样return self.data._value
之类的东西,事情变得讨厌 - 就像@BrenBarn在我对我的帖子的评论中说的那样。我使用inspect.getsourcelines(A.value.fget)
函数来获取包含返回值的源代码行并对其进行解析。如果我没有解析字符串,我提出了一个异常。结果看起来像这样:
def make_setter(name, attrname=None):
def setter(self, val):
try:
split_name = name.split('.')
child_attr = getattr(self, split_name[0])
for i in range(len(split_name)-2):
child_attr = getattr(child_attr, split_name[i+1])
setattr(child_attr, split_name[-1], val)
except:
raise Exception("Failed to set property attribute {0}".format(name))
它似乎有效,但可能有错误。
现在问题是,如果事情失败该怎么办?这取决于你和这个问题的偏离轨道。就个人而言,我做了一些讨厌的事情,涉及创建一个继承自A的新类(让我们称之为B类)。然后,如果setter适用于A,它将适用于B的实例,因为A是基类。但是,如果它不起作用(因为A中定义的返回值是令人讨厌的),我在类 B上运行settattr(B, name, val)
。这通常会更改所有其他实例从B创建(就像在这篇文章中的第二个代码块中)但是我使用type('B', (A,), {})
动态创建B并且只使用它一次,因此更改类本身不会影响其他任何内容。
我认为这里有很多黑魔法类型的东西,但是它很酷且在当天使用它非常通用我一直在使用它。这些都不是可复制的代码,但如果您理解它,那么您可以编写修改。
我真的希望/希望有更好的方法,但我不知道。也许从类创建的元类或描述符可以为你做一些不错的魔术,但我还不太了解它们还不确定。
评论赞赏!