I have a Config class. I would like it to have several methods, which depends on another variable of the class. This way I would only have to change one variable in the config class and then it would function differently.
I tried the following. When creating an instance I can access other_param, but the objective function is lost.
class Config:
def __init__(self):
self.task = 'Task1'
if self.task=='Task1':
self.other_param = 1
def objective(self,probs):
return probs[0]
if self.task=='Task2':
self.other_param = 2
def objective(self,probs):
return probs[1]
In fact I can get a solution by reversing the logic, i.e. defining a method and having an if statement within that. But I want several methods for each if branch... and I don't want to have all these methods full of if statements. The code would just be more readable if all the methods belonging to the same if branch would be in the same place.
CodePudding user response:
Something like this should work for your problem:
class Config:
def __init__(self, task: str):
self.task = task
if self.task == 'Task1':
self.other_param = 1
self.objective = lambda probs: probs[0]
elif self.task == 'Task2':
self.other_param = 2
self.objective = lambda probs: probs[1]
>>> a = Config('Task1')
>>> a.objective([1,2])
1
>>> a = Config('Task2')
>>> a.objective([1,2])
2
For multi-lines functions:
class Config:
def __init__(self, task: str):
self.task = task
if self.task == 'Task1':
self.other_param = 1
def objective(probs):
...
return probs[0]
self.objective = objective
elif self.task == 'Task2':
self.other_param = 2
def objective(probs):
...
return probs[1]
self.objective = objective
Note that self
is implicitly declared in the objective
function and can be accessed directly in it.
CodePudding user response:
I would use simple class derivation here, with a static build method in base class.
class Config:
# _subs = {"Task1": Config1, 'Task2': Config2} # subclasses are not defined yet
@classmethod
def build(cls, task):
return cls._subs[task]()
conf.otherparam = task[-1] # or whatever common initialization
class Config1(Config):
def objective(self, probs):
return probs[0]
class Config2(Config):
def objective(self, probs):
return probs[1]
# set _subs static member once subclasse have been defined
Config._subs = {"Task1": Config1, 'Task2': Config2}
You can then use
conf1 = Config.build('Task1')
conf1.objective(probs)
...
You could make the base class abstract to ensure that objective
is overriden.
CodePudding user response:
I think any way of avoiding having conditionally-defined methods on the class is better than doing that.
Looking at your example code, it could be written as:
class Config:
def __init__(self):
self.task = 'Task1'
self._probs_index = 0
if self.task == 'Task1':
self.other_param = 1
if self.task == 'Task2':
self.other_param = 2
self._probs_index = 1
def objective(self, probs):
return probs[self._probs_index]
Probably your real code is more complex and you will say this won't work for you.
But I would suggest to avoid what you're currently trying to do at all costs.
It's not clear if task
is an arg to __init__
in your real code (the example doesn't make much sense at the moment)
If it is we might re-write the above as:
class Config:
def __init__(self, task):
self.task = task
# default values
self._probs_index = 0
self.other_param = 0
if self.task == 'Task1':
self.other_param = 1
elif self.task == 'Task2':
self.other_param = 2
self._probs_index = 1
else:
# unexpected value, choose here if you want to
# continue and use defaults or raise an error
raise ValueError(f"Unexpected task: {task}")
def objective(self, probs):
return probs[self._probs_index]
If it's not then we could think of them as sub-classes. It would be natural to treat classes with related but different behaviour as sub-classes.
from abc import ABC, abstractmethod
class BaseConfig(ABC):
task = None # overwrite in sub-classes
# default values, overwrite in sub-classes if needed
other_param = 0
@abstractmethod
def objective(self, probs):
pass
class Task1Config(BaseConfig):
task = "Task1"
other_param = 1
def objective(self, probs):
return probs[0]
class Task2Config(BaseConfig):
task = "Task2"
other_param = 2
def objective(self, probs):
return probs[1]