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.
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?
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With