Search code examples
pythonmetaprogrammingsetattr

How can I dynamically add a function to an instance in python upon instantiation


If I have a python class which allows for an options parameter upon instantiation, how can I dynamically set a function to it, based upon the value of that options parameter. For example, if I have the code

def hello1():
    print(self.name,"says hi")

def hello2():
    print(self.name,"says hello")

class A:
    def __init__(self, name, opt=0):
        if opt == 1:
            setattr(self,'hello',hello1)
        else:
            setattr(self,'hello',hello2)

if __name__ == "__main__":
    a1 = A("my")
    a2 = A("name",1)
    a1.hello()
    a2.hello()

I get the traceback error

Traceback (most recent call last):
  File "dynamic_classes.py", line 17, in <module>
    a1.hello()
  File "dynamic_classes.py", line 5, in hello2
    print(self.name,"says hello")
NameError: global name 'self' is not defined

Solution

  • Your functions do not define a self parameter, nor will they ever get one.

    You need to use methods; you can create these from the functions by treating them as descriptors and explicitly calling .__get__() on them:

    def hello1(self):
        print(self.name,"says hi")
    
    def hello2(self):
        print(self.name,"says hello")
    
    class A:
        def __init__(self, name, opt=0):
            if opt == 1:
                setattr(self, 'hello', hello1.__get__(self, type(self))
            else:
                setattr(self, 'hello', hello2.__get__(self, type(self)))
    

    Normally, the .__get__() method is called on functions when accessing them on a class (either directly or via an instance). This doesn't happen for functions added directly on an instance however, so you need to do it manually.