3

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

import mat
class Klass(object):

    @mat.sell(mat.CanSet):
    def method1(self):
        return None

    @mat.sell(mat.CanSet):
    def method2(self):
        return 'value2'

想象一下,我需要为此“Klass”填充 10 种方法。我想生成这些方法而不明确地全部写出来。所以我想做一个为每种方法做 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”的建议,也将受到欢迎。

4

2 回答 2

6

Myk Willis 的回答解释了 method_value 变量范围的问题。我将尝试扩展它以回答您的其他疑问。

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

所以这:

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

知道了这一点,修改 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() 的变化;我们不是直接返回构造的方法,而是首先将它传递给装饰器并返回它的返回值。

至于这个:

另外,任何人都可以帮助如何实现具有一个属性的装饰器吗?PS:如果您有不使用“setattr”的建议,也将受到欢迎。

我不确定我明白你在问什么。你可能想把你的方法变成属性吗?你能告诉我们 mat.sell 是做什么的吗?它可能有助于我们更清楚地了解您正在尝试做什么。

于 2015-10-14T21:15:33.873 回答
6

当您使用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 

在这里,在 lambda 声明时val分配 的值,为 lambda的每个实例赋予其自己的值。这使您可以使用更紧凑的:method_value

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))
于 2015-10-14T20:22:48.113 回答