Home > Software engineering >  Python conditional statement within a Class
Python conditional statement within a Class

Time:10-23

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

  • Related