我已经在python中遇到了这个问题一段时间了: 我需要在类中创建一个方法列表,这些方法几乎完全相同但具有不同的实例成员变量。 所以我的第一次尝试是这样的:
from functools import partial
class Operations:
def __init__(self):
self.a = 10
self.b = 20
self.c = 30
self.add_operation_1 = partial(self.generic_add_1, 'a', 'b')
self.add_operation_2 = partial(self.generic_add_1, 'a', 'c')
def generic_add_1(self, first, second):
first_value = getattr(self, first)
second_value = getattr(self, second)
setattr(self, first, first_value + second_value)
instance = Operations()
instance.add_operation_1()
print(instance.a)
# Should print 30
instance.add_operation_2()
print(instance.a)
# Should print 60
如您所见,我使用getattr和setattr来引用我需要更改的属性。
这很有效,但实际上很慢,因为partial只保留参数,当调用函数时,它会将它们传递给原始函数。此外,我不确定这一点,但是没有比使用像object.property
所以我设法再次尝试:
class Operations:
def __init__(self):
self.a = 10
self.b = 20
self.c = 30
self.add_operation_1 = self.generic_add('a', 'b')
self.add_operation_2 = self.generic_add('a', 'c')
def generic_add(self, first, second):
first_value = getattr(self, first)
second_value = getattr(self, second)
def real_operation():
setattr(self, first, first_value + second_value)
return real_operation
instance = Operations()
instance.add_operation_1()
print(instance.a)
# Should print 30
instance.add_operation_2()
print(instance.a)
# Should print 60 but returns 40 instead!!!
这次我没有使用偏见而是使用闭包。
主要的优点是getattr
只在创建实例对象时执行一次而不是在调用方法时执行,但我找不到摆脱setattr
的方法。
作为副作用,这并不像我预期的那样有效。 getattr
在开头获取属性的值,因此返回的函数不会看到对这些属性的任何更改。
所以现在我有点卡住了。 有没有办法生成这样的方法:
def expected_function(self):
self.a = self.a + self.b
给出属性名称?
感谢。
答案 0 :(得分:2)
def function_generate(v, s1, s2):
def f():
v[s1] += v[s2]
return f
class Operations:
def __init__(self):
self.a = 10
self.b = 20
self.c = 30
namespace = vars(self)
self.add_operation_1 = function_generate(namespace, 'a', 'b')
self.add_operation_2 = function_generate(namespace, 'a', 'c')
instance = Operations()
instance.add_operation_1()
print(instance.a)
# Should print 30
instance.add_operation_2()
print(instance.a)
# Should print 60
答案 1 :(得分:0)
正如dospro在他的评论中指出的那样,getattr只执行一次就是一个bug。您的闭包将在后续调用中使用过时的值。
关于性能,您应该直接使用__dict__属性而不是使用setattr / getattr获得一些。
为了获得一个为什么直接访问__dict__比getattr / setattr更快的提示,我们可以查看生成的字节码:
self.__dict__['a'] = 1
0 LOAD_CONST 1 (1)
2 LOAD_FAST 0 (self)
4 LOAD_ATTR 0 (__dict__)
6 LOAD_CONST 2 ('a')
8 STORE_SUBSCR
setattr(self, 'a', 1)
0 LOAD_GLOBAL 0 (setattr)
2 LOAD_FAST 0 (self)
4 LOAD_CONST 1 ('a')
6 LOAD_CONST 2 (1)
8 CALL_FUNCTION 3
10 POP_TOP
setattr转换为函数调用,而写入__dict__是一个存储操作。
答案 2 :(得分:0)
好的经过大量的实验后,我找到了一个解决方案,虽然它很不优雅。
def generic_eval_add(self, first, second):
my_locals = {
'self': self
}
string_code = """def real_operation():
self.{0} = self.{0} + self.{1}""".format(first, second)
print(string_code)
exec(string_code, my_locals)
return my_locals['real_operation']
由于这可以在初始化时进行评估,因此它正是我所需要的。最大的折衷是优雅,可读性,错误处理等。 我认为Paul Cornelius解决方案对于这个用例来说已经足够了。 虽然我可能会考虑使用jinja来生成python代码
谢谢你的帮助。