Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

assigning value in python dict (copy vs reference)

I understand that in python every thing, be it a number, string, dict or anything is an object. The variable name simply points to the object in the memory. Now according to this question,

>> a_dict = b_dict = c_dict = {}

This creates an empty dictionary and all the variables point to this dict object. So, changing any one would be reflected in the other variables.

>> a_dict["key"] = "value" #say
>> print a_dict
>> print b_dict
>> print c_dict

would give

{'key': value}
{'key': value}
{'key': value}

I had understood the concept of variables pointing to objects, so this seems fair enough.

Now even though it might be weird, since its such a basic statement, why does this happen ?

>> a = b = c = 1
>> a += 1
>> print a, b, c
2, 1, 1   # and not 2, 2, 2

First part of question: Why isn't the same concept applied here ?

Actually this doubt came up when I was trying to search for a solution for this:

>> a_dict = {}
>> some_var = "old_value"
>> a_dict['key'] = some_var
>> some_var = "new_value"
>> print a_dict
{'key': 'old_value'}  # and not {'key': 'new_value'}

This seemed counter-intuitive since I had always assumed that I am telling the dictionary to hold the variable, and changing the object that the variable was pointing to would obviously reflect in the dictionary. But this seems to me as if the value is being copied, not referenced. This was the second thing I didn't understand.

Moving on, i tried something else

>> class some_class(object):
..    def __init__(self):
..        self.var = "old_value"
>> some_object = some_class()
>> a_dict = {}
>> a_dict['key'] = some_object
>> some_object.var = "new_value"
>> print a_dict['key'].var
"new_value"    # even though this was what i wanted and expected, it conflicts with the output in the previous code

Now, over here, obviously it was being referenced. These contradictions has left me squacking at the unpredictable nature of python, even though I still love it, owing to the fact I don't know any other language well enough :p . Even though I'd always imagined that assignments lead to reference of the object, however these 2 cases are conflicting. So this is my final doubt . I understand that it might be one those python gotcha's . Please educate me.

like image 277
darkryder Avatar asked May 25 '14 05:05

darkryder


People also ask

Are dictionaries in Python passed by reference or value?

In essence, one can say that mutable objects like dictionaries, sets, and lists are passed by reference. Immutable objects like int , str , tuple are passed by value.

Does copy () work on dictionaries in Python?

Use copy()This is a built-in Python function that can be used to create a shallow copy of a dictionary. This function takes no arguments and returns a shallow copy of the dictionary. When a change is made to the shallow copy, the original dictionary will remain unchanged.

Should I use dict () or {}?

With CPython 2.7, using dict() to create dictionaries takes up to 6 times longer and involves more memory allocation operations than the literal syntax. Use {} to create dictionaries, especially if you are pre-populating them, unless the literal syntax does not work for your case.


2 Answers

You're wrestling with 2 different things here. The first is the idea of mutability vs. immutability. In python, str, int, tuple are some of the builtin immutable types compared to list, dict (and others) which are mutable types. immutable objects are ones which cannot be changed once they are created. So, in your example:

a = b = c = 1

After that line, all a, b and c refer to the same integer in memory (you can check by printing their respecitve id's and noting that they are the same). However, when you do:

a += 1

a now refers to a new (different) integer at a different memory location. Note that as a convention, += should return a new instance of something if the type is immutable. If the type is mutable, it should change the object in place and return it. I explain some of the more gory detail in this answer.


For the second part, you're trying to figure out how python's identifiers work. The way that I think of it is this... when you write a statement:

name = something

The right hand side is evaluated into some object (an integer, string, ...). That object is then given the name on the left hand side1. When a name is on the right hand side, the corresponding object is automatically "looked up" and substituted for the name in the calculation. Note that in this framework, assignment doesn't care if anything had that name before -- it simply overwrites the old value with the new one. Objects which were previously constructed using that name don't see any changes -- either. They've already been created -- keeping references to the objects themselves, not the names. So:

a = "foo"  # `a` is the name of the string "foo" 
b = {"bar": a}  # evaluate the new dictionary and name it `b`.  `a` is looked up and returns "foo" in this calculation
a = "bar"  # give the object "bar" the name `a` irrespecitve of what previously had that name

1I'm glossing over a few details here for simplicity -- e.g. what happens when you assign to a list element: lst[idx] = some_value * some_other_value.

like image 116
mgilson Avatar answered Oct 16 '22 18:10

mgilson


This is because += can be interpreted as a = a + 1, which rebinds the variable a to the value a + 1, that is, 2.

Similarly, some_var = "new_value" rebinds the variable and the object is not changed, so the key, value pair in the dictionary still points to that object.

In your last example, you are not rebinding, but mutating the object, so the value is changed in the dictionary.

like image 37
Sukrit Kalra Avatar answered Oct 16 '22 19:10

Sukrit Kalra