I have some objects that have a dictionary of attributes, obj.attrs
. The constructor for these objects accepts a dict and/or **kwargs
, for convenience.
It looks a little like this:
class Thing:
def __init__(self, attrs={}, **kwargs):
for arg in kwargs:
attrs[arg] = kwargs[arg]
self.attrs = attrs
Such that Thing({'color':'red'})
does the same as Thing(color='red')
.
My problem is that the constructor somehow remembers the last attrs
value passed to it.
For example:
>>> thing1 = Thing(color='red')
>>> thing2 = Thing()
>>> thing2.attrs
{'color': 'red'}
...but thing2.attrs
should just be an empty dict! {}
This made me wonder if this isn't a problem with using both **kwargs
and an argument like attrs={}
.
Any ideas?
The problem with using default arguments is that only one instance of them actually exists. When you say attrs={}
in your init method definition, that single default {} instance is the default for every call to that method (it doesn't make a new default empty dict every time, it uses the same one).
The problem is that if there's only one attrs
in existence and then for every instance of Thing you say self.attrs = attrs
, the self.attrs
member variable for every single one of your instance is pointing to the single shared default instance of attrs
.
The other question is, isn't this completely redundant? You can use **kwargs
to pass in keyword/value args or a dictionary. If you just defined this:
class Thing:
def __init__(self, **kwargs):
for arg in kwargs:
self.attrs[arg] = kwargs[arg]
All of these strategies still work:
thing1 = Thing(color='red')
thing2 = Thing(**{'color':'red'})
my_dict = {'color' : 'red'}
thing3 = Thing(**my_dict)
So if you simply define and use Thing that way, you'll avoid your problem altogether.
attrs
is a reference to a dictionary. When you create a new object, self.attrs
points to that dictionary. When you assign a value from a kwargs
, it goes to this dictionary.
Now, when you create a second instance, it's self.attrs
also points to that same dictionary. Thus, it gets whatever data is in that dictionary.
For a nice discussion of this see "Least Astonishment" in Python: The Mutable Default Argument here on stackoverflow. Also see Default Parameter Values in Python on effbot.
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