Possible Duplicate:
“Least Astonishment” in Python: The Mutable Default Argument
I'm very confused about the behavior of dictionaries as class instance variables in Python 3. The way I understand it, instance variables in Python have per-instance storage, unlike class variables which are per-class (similar to what some other languages call "static").
And this seems to hold true, except when the instance variable is a dictionary created from a default parameter. For example:
class Foo:
def __init__(self, values = dict()):
self.values = values
f1 = Foo()
f1.values["hello"] = "world"
f2 = Foo()
print(f2.values)
This program outputs:
{'hello': 'world'}
Huh? Why does the instance f2
have the same dictionary instance as f1
?
I get the expected behavior if I don't pass in an empty dictionary as a default parameter, and just assign self.values
to an empty dictionary explicitly:
class Foo:
def __init__(self):
self.values = dict()
But I can't see why this should make any difference.
You can assign a dictionary value to a variable in Python using the access operator [].
The dict. copy() method returns a shallow copy of the dictionary. The dictionary can also be copied using the = operator, which points to the same object as the original. So if any change is made in the copied dictionary will also reflect in the original dictionary.
Use a for loop If you change this copy of the dictionary, it will not change the original dictionary. However, if one of the elements is an iterable object and a change is made to that element in the shallow copy, the same change will be reflected in the original copy.
Copy a dictionary with a for loop To copy a dictionary it is also possible to use a for loop: >>> d1 = {'a':1,'b':2} >>> d2 = {} >>> for key in d1: ... d2[key] = d1[key] ...
This is a well known surprise in Python. The default parameters are evaluated when the function is defined, not when it is called. So your default parameter is a reference to a common dict
. It has nothing to do with assigning it to class/instance variables.
If you want to use a default parameter, use None
, and check it:
if values is None:
self.values = {}
else:
self.values = values
Default values are only evaluated once. You want something like this:
class Foo:
def __init__(self, values = None):
self.values = values or dict()
If you supply a values
, that'll get used. If not, values
is evaluated as FALSE
by the or
operator and instantiates a new dict.
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