I have a class which has multiple methods defined.
import mat
class Klass(object):
@mat.sell(mat.CanSet):
def method1(self):
return None
@mat.sell(mat.CanSet):
def method2(self):
return 'value2'
Imagine I have 10 methods that I need to populate for this 'Klass'. I want to generate these methods without explicitely writing them all. So I want to do a factory that does setattr for each method. Problem is that I do following and the last method has the last value. Each do not get its related value but value10. Also below solution does not implement the decorator, I have no idea how to do assign the 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)
So when I do following....
k = Klass()
print k.method1(), method2()...., method10()
it all results in value10 for each method. Do not understand why ? 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.
When you use the lambda
to create each method, you are binding it to the currently-local scope. That scope has a single instance of a variable named method_value
, and it is being set to a new value after each loop. Because each lambda
refers to this one local variable, they all see the same value (e.g., the last value that was set).
If you create the lambda in a different method, it will have a different scope, and you can therefore get the desired behavior:
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"
Here, make_attr
creates a new scope, and so there are unique instances of the variable value
, one for each lambda created.
You can also use a nice trick to create a lambda-scoped variable inline, as follows:
lambda self, val=method_value: val
Here, val
is assigned the value of method_value
at the time of the lambda declaration, giving each instance of lambda its own value. This lets you use the more compact:
for method_name, method_value in list1:
setattr(Klass, method_name, lambda self, val=method_value:val))
Finally, Marti points out in a different answer how to modify my make_attr
function to apply the desired decorator:
def make_attr(value):
return mat.sell(mat.CanSet)(lambda self: value))