Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python: programmatic class instance variable initialisation with locals()

I have a class with many instance variables with default values, which optionally can be overridden in instantiantion (note: no mutable default arguments).

Since it's quite redundant to write self.x = x etc. many times, I initialise the variables programmatically.

To illustrate, consider this example (which has, for the sake of brevity, only 5 instance variables and any methods omitted):

Example:

# The "painful" way
class A:
    def __init__(self, a, b=2, c=3, d=4.5, e=5):
        self.a = a
        self.b = b
        self.c = c
        self.d = d
        self.e = e

# The "lazy" way
class B:
    def __init__(self, a, b=2, c=3, d=4.5, e=5):
        self.__dict__.update({k: v for k, v in locals().items() if k!='self'})

# The "better lazy" way suggested
class C:
    def __init__(self, a, b=2, c=3, d=4.5, e=5):
            for k, v in locals().items():
                if k != 'self':
                    setattr(self, k, v)

x = A(1, c=7)
y = B(1, c=7)
z = C(1, c=7)

print(x.__dict__)  # {'d': 4.5, 'c': 7, 'a': 1, 'b': 2, 'e': 5}
print(y.__dict__)  # {'d': 4.5, 'c': 7, 'a': 1, 'b': 2, 'e': 5}
print(z.__dict__)  # {'d': 4.5, 'c': 7, 'a': 1, 'b': 2, 'e': 5}

So to make my life easier, I use the idiom shown in class B, which yields the same result as A.

Is this bad practice? Are there any pitfalls?

Addendum: Another reason to use this idiom was to save some space - I intended to use it in MicroPython. For whatever reason Because locals work differently there, only the way shown in class A works in it.

like image 962
phoibos Avatar asked Dec 15 '25 18:12

phoibos


2 Answers

I would actually suggest using the code shown in class A. What you have is repetitive code, not redundant code, and repetitive isn't always bad. You only have to write __init__ once, and keeping one assignment per instance variable is good documentation (explicit and clear) for what instance variables your class expects.

One thing to keep in mind, though, is that too many variables that you can initialize as distinct parameters may be a sign that your class needs to be redesigned. Would some of the individual parameters make more sense being grouped into separate lists, dicts, or even additional classes?

like image 68
chepner Avatar answered Dec 17 '25 08:12

chepner


Try a more pythonic approach:

class C:
  def __init__(self,a,b=2,c=3,d=4.5,e=5):
    for k,v in locals().iteritems():
      setattr(self,k,v)
c = C(1)
print c.a, c.b
1 2

This approach may be a line or two longer, but the line lengths are shorter, and your intent less convoluted. In addition, anyone who may try to reuse your code will be able to access your objects' attributes as expected.

Hope this helps.

Edit: removed second approach using kwargs bc it does not address default variable requirement.

the important take-away here is that a user of your code would not be able to access your object's attributes as expected if done like your example class B shows.

like image 27
Anna B Nana Avatar answered Dec 17 '25 08:12

Anna B Nana



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!