用于带装饰器的动态方法创建者的python setattr

用于带装饰器的动态方法创建者的python setattr,python,decorator,factory,python-decorators,setattr,Python,Decorator,Factory,Python Decorators,Setattr,我有一个定义了多个方法的类 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”填充。我想生成这些方法,而不需要明确地编写它们。所以我想创建一个工厂,为

我有一个定义了多个方法的类

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。问题是我执行以下操作,最后一个方法具有最后一个值。每个都不获取其相关值,但获取值10。下面的解决方案也没有实现decorator,我不知道如何分配decorator

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()
每种方法的结果都是值10。不明白为什么? 另外,有人能帮助我们如何用一个属性实现decorator吗?
PS:如果您有不使用“setattr”的建议,也会受到欢迎。

当您使用
lambda
创建每个方法时,您将其绑定到当前的本地范围。该范围有一个名为
method\u 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声明时被分配了
方法的值
,为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 answer解释了method_值变量范围的问题。我将尝试对此进行详细阐述,以回答您的其他疑问

关于decorator,以及在以编程方式生成方法时如何应用decorator:一旦您了解了decorator在Python中的工作方式,就变得更容易了。问题是,它们只是函数。装饰器是接收函数并返回函数的函数。@syntax只是一种语法糖,它以更方便的方式将指定的修饰符应用于修饰函数

因此:

@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不是一个decorator,而是一个返回decorator的函数;因此有了双重调用:首先我们调用mat.sell,将mat.CanSet参数传递给它,然后该调用返回一个用于包装method1的装饰器

知道了这一点,修改Myk的答案以应用您的decorator很简单:

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()中的更改;我们不是直接返回虚构的方法,而是首先将其传递给装饰器,然后返回其返回值

关于这一点:

另外,有人能帮助我们如何用一个属性实现decorator吗?PS:如果您有不使用“setattr”的建议,我们也会欢迎您的


我不确定我是否理解你的要求。您是否希望将方法转换为属性?你能告诉我们mat.sell是做什么的吗?这可能有助于我们更清楚地了解您正在尝试做的事情。

有趣的是,如果您这样做:
list1=[('method1',lambda x:'value1'),('method2',lambda x:'value2'),('method10',lambda x:'value10')]
它可以工作。我只能在这里猜测。。。我认为在第一种情况下,lambda最终是一个函数(覆盖它,从而产生最后一个值)。但我不确定。哇,谢谢,我怀疑在操作码中发生了类似的事情。。Lambdas确实是ScaryTanks a bunch Myk,这解决了方法值,知道我如何为生成的每个方法使用此装饰器吗?@LuckyStarr我编辑了答案,显示了如何应用所需的装饰器Tanks a bunch进行解释Marti,它不仅详细解释了装饰器,而且解决了问题。不过,如果我可以在“def make_attr(value):”的顶部应用decorator,并简单地返回value来代替您的建议,那将是一件有趣的事情。我今天要试一试。