dynamically create methods in python

Question:

given the following snippet:

def fun(ret):
    return ret

class A:
    def __init__(self):
        for string in ['a', 'b']:
            setattr(self, string, lambda: fun(string))

>>> a = A()
>>> a.a()
'b'

I want a method a() which returns ‘a’ and a method b() which returns ‘b’. As long as I don’t use a lambda expression but setting the attribute to a simple string, the association is correct.

I think my intention is clear? So where am I wrong?

Asked By: hinerk

||

Answers:

In Python, a function will lookup non-local names in the scope where it was defined or in the global scope if the name still does not exist there. If the value associated to the name changed, so will the returned value. Note that this is not specific to lambda functions.

A way around this is to create a closure by writing a helper function.

def fun(ret):
    return ret

class A:
    def __init__(self):
        def apply_fun(item):
            return lambda: fun(item)

        for string in ['a', 'b']:
            setattr(self, string, apply_fun(string))

print(A().a())  # 'a'

Alternative solution

In that particular case, using __getattr__ might be more suited as it is intended to dynamically return attributes.

def fun(ret):
    return ret

class A:
    def __getattr__(self, item):
        if item in ['a', 'b']:
            return lambda: fun(item)

print(A().a())  # 'a'
Answered By: Olivier Melançon
Categories: questions Tags:
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.