I wanted objects to call a certain method (the specific one being assigned in a variable) in their routine. I managed to do it with a dict and I also managed to do it by assigning the variable after object creation. Which way is better? Is there another cleaner way?
First way that works (dict):
class Foo:
def __init__(self, name, funcin) -> None:
self.name = name
self.func = {
'bear': self.bear,
'boar': self.boar,
}[funcin]
def routine(self):
print('fluff')
self.func()
def bear(self):
print(f'I, {self.name}, am a bear and I like honey.')
def boar(self):
print(f'I, {self.name}, am a boar and I like fruits.')
bob = Foo('bob', 'bear')
mark = Foo('mark', 'boar')
bob.routine()
mark.routine()
Second way that worked (another line to assign):
class Foo:
def __init__(self, name) -> None:
self.name = name
def routine(self):
print('fluff')
self.func()
def bear(self):
print(f'I, {self.name}, am a bear and I like honey.')
def boar(self):
print(f'I, {self.name}, am a boar and I like fruits.')
Kona = Foo('Kona')
Kona.func = Kona.bear
John = Foo('John')
John.func = John.boar
Kona.routine()
John.routine()
Ways that didn't work:
# NameError: name 'bear' is not defined
class Foo:
def __init__(self, name, funcin) -> None:
self.name = name
self.func = self.funcin
def bear(self):
print(f'I, {self.name}, am a bear and I like honey.')
bob = Foo('bob', bear)
bob.func()
# NameError: name 'self' is not defined
class Foo:
def __init__(self, name, funcin) -> None:
self.name = name
self.func = funcin
def bear(self):
print(f'I, {self.name}, am a bear and I like honey.')
bob = Foo('bob', self.bear)
bob.func()
# NameError: name 'Kim' is not defined
class Foo:
def __init__(self, name, funcin) -> None:
self.name = name
self.func = funcin
def bear(self):
print(f'I, {self.name}, am a bear and I like honey.')
Kim = Foo('Kim', Kim.bear)
Kim.func()
# TypeError: Foo.bear() missing 1 required positional argument: 'self'
class Foo:
def __init__(self, name, funcin) -> None:
self.name = name
self.func = funcin
def bear(self):
print(f'I, {self.name}, am a bear and I like honey.')
bob = Foo('bob', Foo.bear)
bob.func()
CodePudding user response:
The class's methods are available just as they are in a dict via the class's __dict__
attribute. So in the first solution you presented, you could also define self.func
like this:
self.func = Foo.__dict__[funcin].__get__(self)
So with __dict__[funcin]
we retrieve the method from the class object, and with __get__
we we get a function that has the correct self
binding.
To make it even more generic so that you don't have to mention Foo
:
self.func = self.__class__.__dict__[funcin].__get__(self)
CodePudding user response:
A common solution is class inheritance. There are multiple options from abstract base classes to mixins, but here is a simple start
class Foo:
def __init__(self, name) -> None:
self.name = name
def func(self):
raise NotImplementedError
def routine(self):
print('fluff')
self.func()
class Bear(Foo):
def func(self):
print(f'I, {self.name}, am a bear and I like honey.')
class Boar(Foo):
def func(self):
print(f'I, {self.name}, am a boar and I like fruits.')
bob = Bear('bob')
mark = Boar('mark')
bob.routine()
mark.routine()
CodePudding user response:
You could use getattr():
class Foo:
def __init__(self, name, funcin) -> None:
self.name = name
self.func = getattr(self,funcin)
def routine(self):
print('fluff')
self.func()
def bear(self):
print(f'I, {self.name}, am a bear and I like honey.')
def boar(self):
print(f'I, {self.name}, am a boar and I like fruits.')
If you don't need to perform additional (common) fluff, you could simply assign self.routine = getattr(self,funcin)
and not even define the routine() function.