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 是做什么的吗?这可能有助于我们更清楚地了解您正在尝试做什么。