2

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
user1876508
  • 12,864
  • 21
  • 68
  • 105
  • Do you realize that `getattr(a, 'b', c)` is an obscure way to write `a.b = c`? –  Aug 06 '13 at 22:03
  • I understand that. I am writing a wrapper around zmq which instantiates processes with a special connection method, either a bind, or connect method, which is defined at instantiation – user1876508 Aug 06 '13 at 22:06
  • So, why do you use `setattr` (just noticed the typo in my first comment), rather than plain old attribute assignment? –  Aug 06 '13 at 22:07
  • Here is a copy of my current code http://pastebin.com/9QeY0p0y, maybe this will illuminate this. I do not want to have to write a connect method for each class, also, I will add extra protection to the bind method – user1876508 Aug 06 '13 at 22:09
  • I am *only* talking about the lines of the form `setattr(self, 'connect', )`. These are exactly equivalent to `self.connect = `, so why don't you do that? –  Aug 06 '13 at 22:11

2 Answers2

5

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.

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
0

You can also use MethodType from types module.

import types

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

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

class A:
   def __init__(self, name, opt=0):
       self.name = name
       if opt == 1:
           self.hello = types.MethodType(hello1, self)
       else:
           self.hello = types.MethodType(hello2, self)

A('a').hello()   # ('a', 'says hello')

A('a', 1).hello() # ('a', 'says hi')
Harshal Dhumal
  • 1,259
  • 1
  • 10
  • 18