我有一个定义了多个方法的类。
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)
想象一下,我需要为这个'Klass'填充10个方法。我想生成这些方法而不是明确地写出它们。所以我想做一个为每个方法做setattr的工厂。问题是我做了以下,最后一个方法有最后一个值。每个都没有得到它的相关值,但值10。另外下面的解决方案没有实现装饰器,我不知道如何分配装饰器
k = Klass()
print k.method1(), method2()...., method10()
所以,当我关注....
$xml_catalog=[xml]@'
<catalogue date_maj="2015-10-10T19:04:51">
<products>
<product id="1">Pdt1</product>
<product id="2">Pdt2</product>
</products>
<categories>
<category id="1">cat1</category>
<category id="2">cat2</category>
</categories>
</catalogue>
'@
$xml_catalog_target = [xml]@'
<?xml version="1.0" encoding="iso-8859-1"?>
<catalogue date_maj="2015-10-12T19:04:51">
</catalogue>
'@
Foreach ($Node in $xml_catalog.DocumentElement.ChildNodes) {
$xml_catalog_target.DocumentElement.AppendChild($xml_catalog.ImportNode($Node, $true))
}
每个方法都会产生值10。不明白为什么 ? 另外,任何人都可以帮助如何使用一个属性实现装饰器? PS:如果你有不使用'setattr'的建议,那也会受到欢迎。
答案 0 :(得分:4)
使用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在a different answer中指出如何修改我的make_attr
函数以应用所需的装饰器:
def make_attr(value):
return mat.sell(mat.CanSet)(lambda self: value))
答案 1 :(得分:4)
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参数,然后该调用返回一个我们用来包装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()的变化;我们不是直接返回伪造的方法,而是先将它传递给装饰器,然后返回它的返回值。
至于此:
另外,任何人都可以帮助如何使用一个属性实现装饰器? PS:如果您有不使用&#39; setattr&#39;的建议,那也会受到欢迎。
我不确定我明白你在问什么。您是否想要将方法转换为属性?你能告诉我们mat.sell的作用吗?这可能有助于我们更清楚地了解您的目标。