I would like to create a class whose f method depends on the "mode" the object of the class has been created.
The code below doesn't work but hope it gets you an idea of what I am trying to do. My idea is to have a dictionary in which I define the settings for each mode (in this case the function or method to assign to self.f, so that rather than using many if elif statements
in the init function I just assign the correct values using the dictionary.
class A(object):
_methods_dict={
'a':A.f1,
'b':A.f2
}
def __init__(self,mode = 'a'):
self.f = _methods_dict[mode]
def f1(self,x):
return x
def f2(self,x):
return x**2
I can't figure why this does not work, how would you fix it?
Also are there better (and more pythonic) approaches to get the same kind of functionalities?
Store the name of the two functions, then use getattr()
to retrieve the bound method in __init__
:
class A(object):
_methods_dict = {
'a': 'f1',
'b': 'f2'
}
def __init__(self, mode='a'):
self.f = getattr(self, self._methods_dict[mode])
def f1(self, x):
return x
def f2(self, x):
return x ** 2
Alternatively, just proxy the method:
class A(object):
_methods_dict = {
'a': 'f1',
'b': 'f2'
}
def __init__(self,mode = 'a'):
self._mode = mode
@property
def f(self):
return getattr(self, self._methods_dict[self._mode])
def f1(self, x):
return x
def f2(self, x):
return x ** 2
The f
property just returns the correct bound method for the current mode. Using a property simplifies call signature handling, and gives users the actual method to introspect if they so wish.
Either method has the same end-result:
>>> a1 = A()
>>> a2 = A('b')
>>> a1.f(10)
10
>>> a2.f(10)
100
The difference lies in what is stored in the instance, the first method stores bound methods:
>>> vars(a1)
{'f': <bound method A.f1 of <__main__.A object at 0x10aa1ec50>>}
>>> vars(a2)
{'f': <bound method A.f2 of <__main__.A object at 0x10aa1ed50>>}
versus the method in the other:
>>> vars(a1)
{'_mode': 'a'}
>>> vars(a2)
{'_mode': 'b'}
That may not seem much of a difference, but the latter method creates instances that can be pickled and deep-copied without problems.
You could just make two separate classes:
class Base(object):
# place here all attributes shared in common among the Modes
pass
class ModeA(Base):
def f(self, x):
return x
class ModeB(Base):
def f(self, x):
return x**2
def make_mode(mode, *args, **kwargs):
mode_dict = {'a':ModeA, 'b':ModeB}
return mode_dict[mode](*args, **kwargs)
a = make_mode('a')
print(a.f(10))
# 10
b = make_mode('b')
print(b.f(10))
# 100
To answer your first question ("why this does not work"): the class object "A" is only created and bound to the module name "A" after the whole class statement (yes, "class" is an executable statement) block has ended, so you cannot refer to neither the name or the class object itself within this block.