What is the best way to achieve having class attributes that change, based on input that has been passed in via a cli argument?
What I've written works perfectly fine, however feels overly messy and bad design. Is there a better way to go about this?
The example below has four different inputs, however as this program grows larger this would become very messy.
Here's some short example code;
class Example(object):
def __init__(self, var1):
self.var1 = var1
self.var2 = None
self.var3 = None
self.var4 = None
if var1 == 'foo':
self.var2 = 'foo2'
self.var3 = 'foo3'
self.var4 = 'foo4'
elif var1 == 'bar':
self.var2 = 'bar2'
self.var3 = 'bar3'
self.var4 = 'bar4'
elif var1 == 'foobar':
self.var2 = 'foobar2'
self.var3 = 'foobar3'
self.var4 = 'foobar4'
elif var1 == 'barfoo':
self.var2 = 'barfoo2'
self.var3 = 'barfoo3'
self.var4 = 'barfoo4'
def main()
parser = argparse.ArgumentParser()
parser.add_argument('input')
args = parser.parse_args()
example = Example(args.input)
print(example.var2) # returns 'foo2'
Example of calling with an argument;
python main.py foo
CodePudding user response:
There are many ways to clean this up, as suggested in the comments. For something of the size and scale of your example, I would do something like this:
class Example:
_DEPENDENT_VALS = {
'foo': ('foo2', 'foo3', 'foo4'),
'bar': ('bar2', 'bar3', 'bar4'),
'foobar': ('foobar2', 'foobar3', 'foobar4'),
'barfoo': ('barfoo2', 'barfoo3', 'barfoo4')
}
def __init__(self, var1):
self.var1 = var1
self.var2 = None
self.var3 = None
self.var4 = None
if var1 in Example._DEPENDENT_VALS:
self.var2, self.var3, self.var4 = Example._DEPENDENT_VALS[var1]
This method might not always be appropriate depending on the number and type of values you're setting but it's one way to go about it.
CodePudding user response:
` ls=['foo','bar','foobar','barfoo'] #add the list of prefixes
if self.var1 in ls:
self.var2= f'{self.var1}2'
self.var3= f'{self.var1}3'
self.var4= f'{self.var1}4'
`
CodePudding user response:
Define a dict
mapping prefixes to a tuple
of the three modifiers, then use it to initialize them:
class Example:
_var_suffixes = {'foo': ('1', '2', '3'),
'bar': ('1', '2', '3'),
'foobar': ('1', '2', '3'),
'barfoo': ('1', '2', '3'),
}
def __init__(self, var1):
self.var1 = var1
self.var2 = None
self.var3 = None
self.var4 = None
if var1 in self._var_suffixes:
suf2, suf3, suf4 = self._var_suffixes[var1]
self.var2 = var1 suf2
self.var3 = var1 suf3
self.var4 = var1 suf4
Optionally, this may make more sense to store var2
through var4
as a list
(especially if the number of suffixes is variable by key), in which case you'd do something like this, which is even simpler:
class Example:
_var_suffixes = {'foo': ('1', '2'),
'bar': ('1', '2', '3'),
'foobar': ('1', '2', '3', '4'),
'barfoo': ('1', '2', '3', '4', '5'),
}
def __init__(self, var1):
self.var1 = var1
self.varlist = []
for suf in self._var_suffixes.get(var1, ()):
self.varlist.append(var1 suf)
# Optional property for access by name
@property
def var2(self):
try:
return self.varlist[0]
except IndexError:
return None
CodePudding user response:
The class body is just Python code. It has specific scope rules, but anything goes otherwise. This means you can create functions conditionally:
class C: if some_condition: def optional_method(self): pass or pull methods from elsewhere:
import some_module
class D: method_name = some_module.function_that_accepts_self etc.
CodePudding user response:
One approach is to keep __init__
as "dumb" as possible, define separate class methods for each "style" of object, and push the burden of selecting the right class method to call to the caller.
class Example:
def __init__(self, var1, var2, var3, var4):
self.var1 = var1
self.var2 = var2
self.var3 = var3
self.var4 = var4
@classmethod
def foo(cls):
return cls('foo', 'foo2', 'foo3', 'foo4')
@classmethod
def bar(cls):
return cls('bar', 'bar2', 'bar3', 'bar4')
# etc
# Instead of Example('foo')
e = Example.foo()
The only thing you lose here is the ability to pass an unknown value to Example
and get a properly initialized value back, for example,
x = "..." # Assumed to be foo, bar, foobar, or barfoo
e = Example(x)
However, the caller can always use whatever method you might have hidden in side Element.__init__
to pick the values for var2
et al. to pick which class method to all. For example,
x = "foo"
e = getattr(Example, x)() # e = Example.foo()
CodePudding user response:
Don't know if the best option but assuming that actual values aren 'foo1' 'foo2'
but something else or more complex I would go for updating the self.dict
Two ways (I think the seccond is more clear)
class A():
def __init__(self,var1):
self.var1 = var1
fooDict = {'var2':'foo2','var3':'foo3','var4':'foo4'}
foovarDict = {'var2':'foovar2','var3':'foovar3','var4':'foovar4'}
if var1 == 'foo':
self.__dict__ = {**self.__dict__,**fooDict}
elif var1 == 'foovar':
self.__dict__ = {**self.__dict__,**foovarDict}
def getVars(self):
print(self.var1)
print(self.var2)
print(self.var3)
print(self.var4)
a = A('foo')
a.getVars()
#ouputs
#foo
#foo2
#foo3
#foo4
b = A('foovar')
b.getVars()
#outputs
#foovar
#foovar2
#foovar3
#foovar4
Second way:
class A():
def __init__(self,var1):
self.var1 = var1
fooDict = {'var2':'foo2','var3':'foo3','var4':'foo4'}
foovarDict = {'var2':'foovar2','var3':'foovar3','var4':'foovar4'}
if var1 == 'foo':
self.__dict__.update(fooDict)
elif var1 == 'foovar':
self.__dict__.update(foovarDict)
def getVars(self):
print(self.var1)
print(self.var2)
print(self.var3)
print(self.var4)
a = A('foo')
a.getVars()
b = A('foovar')
b.getVars()
with same outputs