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
2 Answers 2
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.
Comments
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')
getattr(a, 'b', c)is an obscure way to writea.b = c?setattr(just noticed the typo in my first comment), rather than plain old attribute assignment?setattr(self, 'connect', <something>). These are exactly equivalent toself.connect = <something>, so why don't you do that?