python 带有装饰器的动态方法创建者的 setattr
python setattr for dynamic method creator with decorator
我有一个 class,其中定义了多个方法。
import mat
class Klass(object):
@mat.sell(mat.CanSet):
def method1(self):
return None
@mat.sell(mat.CanSet):
def method2(self):
return 'value2'
假设我有 10 个方法需要为此填充 'Klass'。我想生成这些方法而不用显式地编写它们。所以我想做一个为每个方法做 setattr 的工厂。问题是我做了以下操作,最后一个方法具有最后一个值。每个都没有得到它的相关值,而是 value10。同样下面的解决方案没有实现装饰器,我不知道如何分配装饰器
class Klass(object):
pass
list1 = [('method1', 'value1'), ('method2', 'value2').....('method10', 'value10')]
for each in list1:
method_name, method_value = each
setattr(Klass, method_name, lambda self: method_value)
所以当我跟随....
k = Klass()
print k.method1(), method2()...., method10()
每种方法的结果都是 value10。不明白为什么 ?
另外,任何人都可以帮助如何实现具有一个属性的装饰器吗?
PS:如果您有不使用'setattr'的建议,我们也欢迎。
当您使用 lambda
创建每个方法时,您将其绑定到当前本地范围。该范围有一个名为 method_value
的变量实例,并且在每次循环后将其设置为一个新值。因为每个 lambda
都引用这个局部变量,所以它们都看到相同的值(例如,设置的最后一个值)。
如果您使用不同的方法创建 lambda,它将具有不同的作用域,因此您可以获得所需的行为:
class Klass(object):
pass
list1 = [('method1', 'value1'), ('method2', 'value2'), ('method10', 'value10')]
def make_attr(value):
return lambda self: value
for method_name, method_value in list1:
setattr(Klass, method_name, make_attr(method_value))
c = Klass()
print c.method1(), c.method2(), c.method10() # "value1, value2, value10"
在这里,make_attr
创建了一个新的作用域,因此变量 value
有唯一的实例,每个创建的 lambda 都有一个。
您还可以使用一个不错的技巧来创建一个 lambda 范围的内联变量,如下所示:
lambda self, val=method_value: val
此处,val
在 lambda 声明时被赋予了 method_value
的值 ,为每个 lambda 实例赋予了自己的值。这使您可以使用更紧凑的:
for method_name, method_value in list1:
setattr(Klass, method_name, lambda self, val=method_value:val))
最后,Marti 在 中指出如何修改我的 make_attr
函数以应用所需的装饰器:
def make_attr(value):
return mat.sell(mat.CanSet)(lambda self: value))
Myk Willis 的回答解释了 method_value 变量范围的问题。我将尝试对其进行扩展以回答您的其他疑问。
关于装饰器,以及如何在以编程方式生成方法时应用装饰器:一旦您了解了装饰器在 Python 中的工作原理,就会变得更容易。问题是,它们只是函数。装饰器是接收一个函数和returns一个函数的函数。 @ 语法只是语法糖,它以更方便的方式将指示的装饰器应用于装饰函数。
所以这个:
@my_decorator
def my_function():
...
将执行与此完全相同的操作:
def my_function():
...
my_function = my_decorator(my_function)
这同样适用于您的原始示例。这个:
@mat.sell(mat.CanSet):
def method1(self):
return None
相当于:
def method1(self):
return None
method1 = mat.sell(mat.CanSet)(method1)
这个有点复杂,因为mat.sell不是装饰器,而是return装饰器的函数;因此双重调用:首先我们调用 mat.sell,向它传递 mat.CanSet 参数,然后调用 returns 一个我们用来包装 method1.
的装饰器
了解这一点,修改 Myk 的答案以应用你的装饰器很简单:
class Klass(object):
pass
list1 = [('method1', 'value1'), ('method2', 'value2'), ('method10', 'value10')]
def make_attr(value):
return mat.sell(mat.CanSet)(lambda self: value)
for method_name, method_value in list1:
setattr(Klass, method_name, make_attr(method_value))
注意 make_attr() 中的变化;我们不是直接 return 构造方法,而是先通过装饰器传递它,然后 return 它的 return 值。
至于这个:
Plus, can anyone help on how to implement the decorator with one attribute ? PS: if you have suggestion that does not use 'setattr', that would be welcomed as well.
我不确定我是否理解您的问题。你可能想把你的方法变成属性吗?你能告诉我们 mat.sell 是做什么的吗?这可能有助于我们更清楚地了解您正在尝试做什么。
我有一个 class,其中定义了多个方法。
import mat
class Klass(object):
@mat.sell(mat.CanSet):
def method1(self):
return None
@mat.sell(mat.CanSet):
def method2(self):
return 'value2'
假设我有 10 个方法需要为此填充 'Klass'。我想生成这些方法而不用显式地编写它们。所以我想做一个为每个方法做 setattr 的工厂。问题是我做了以下操作,最后一个方法具有最后一个值。每个都没有得到它的相关值,而是 value10。同样下面的解决方案没有实现装饰器,我不知道如何分配装饰器
class Klass(object):
pass
list1 = [('method1', 'value1'), ('method2', 'value2').....('method10', 'value10')]
for each in list1:
method_name, method_value = each
setattr(Klass, method_name, lambda self: method_value)
所以当我跟随....
k = Klass()
print k.method1(), method2()...., method10()
每种方法的结果都是 value10。不明白为什么 ? 另外,任何人都可以帮助如何实现具有一个属性的装饰器吗? PS:如果您有不使用'setattr'的建议,我们也欢迎。
当您使用 lambda
创建每个方法时,您将其绑定到当前本地范围。该范围有一个名为 method_value
的变量实例,并且在每次循环后将其设置为一个新值。因为每个 lambda
都引用这个局部变量,所以它们都看到相同的值(例如,设置的最后一个值)。
如果您使用不同的方法创建 lambda,它将具有不同的作用域,因此您可以获得所需的行为:
class Klass(object):
pass
list1 = [('method1', 'value1'), ('method2', 'value2'), ('method10', 'value10')]
def make_attr(value):
return lambda self: value
for method_name, method_value in list1:
setattr(Klass, method_name, make_attr(method_value))
c = Klass()
print c.method1(), c.method2(), c.method10() # "value1, value2, value10"
在这里,make_attr
创建了一个新的作用域,因此变量 value
有唯一的实例,每个创建的 lambda 都有一个。
您还可以使用一个不错的技巧来创建一个 lambda 范围的内联变量,如下所示:
lambda self, val=method_value: val
此处,val
在 lambda 声明时被赋予了 method_value
的值 ,为每个 lambda 实例赋予了自己的值。这使您可以使用更紧凑的:
for method_name, method_value in list1:
setattr(Klass, method_name, lambda self, val=method_value:val))
最后,Marti 在 make_attr
函数以应用所需的装饰器:
def make_attr(value):
return mat.sell(mat.CanSet)(lambda self: value))
Myk Willis 的回答解释了 method_value 变量范围的问题。我将尝试对其进行扩展以回答您的其他疑问。
关于装饰器,以及如何在以编程方式生成方法时应用装饰器:一旦您了解了装饰器在 Python 中的工作原理,就会变得更容易。问题是,它们只是函数。装饰器是接收一个函数和returns一个函数的函数。 @ 语法只是语法糖,它以更方便的方式将指示的装饰器应用于装饰函数。
所以这个:
@my_decorator
def my_function():
...
将执行与此完全相同的操作:
def my_function():
...
my_function = my_decorator(my_function)
这同样适用于您的原始示例。这个:
@mat.sell(mat.CanSet):
def method1(self):
return None
相当于:
def method1(self):
return None
method1 = mat.sell(mat.CanSet)(method1)
这个有点复杂,因为mat.sell不是装饰器,而是return装饰器的函数;因此双重调用:首先我们调用 mat.sell,向它传递 mat.CanSet 参数,然后调用 returns 一个我们用来包装 method1.
的装饰器了解这一点,修改 Myk 的答案以应用你的装饰器很简单:
class Klass(object):
pass
list1 = [('method1', 'value1'), ('method2', 'value2'), ('method10', 'value10')]
def make_attr(value):
return mat.sell(mat.CanSet)(lambda self: value)
for method_name, method_value in list1:
setattr(Klass, method_name, make_attr(method_value))
注意 make_attr() 中的变化;我们不是直接 return 构造方法,而是先通过装饰器传递它,然后 return 它的 return 值。
至于这个:
Plus, can anyone help on how to implement the decorator with one attribute ? PS: if you have suggestion that does not use 'setattr', that would be welcomed as well.
我不确定我是否理解您的问题。你可能想把你的方法变成属性吗?你能告诉我们 mat.sell 是做什么的吗?这可能有助于我们更清楚地了解您正在尝试做什么。