SQA777
SQA777

Reputation: 372

copy/deepcopy and variable scope

I'm having problems using copy.copy() and copy.deepcopy() and Python's scope. I call a function and a dictionary is passed as an argument. The dictionary copies a local dictionary but the dictionary does not retain the values that were copied.

def foo (A, B):
    localDict = {}
    localDict['name'] = "Simon"
    localDict['age'] = 55
    localDict['timestamp'] = "2011-05-13 15:13:22"
    localDict['phone'] = {'work':'555-123-1234', 'home':'555-771-2190', 'mobile':'213-601-9100'}

    A = copy.deepcopy(localDict)

    B['me'] = 'John Doe'
    return



def qua (A, B):
    print "qua(A): ", A
    print "qua(B): ", B

    return


# *** MAIN ***
# 
# Test
#
A = {}
B = {}

print "initial A: ", A
print "initial B: ", B

foo (A, B)

print "after foo(A): ", A
print "after foo(B): ", B

qua (A, B)

The copy.deepcopy works and within function "foo", dict A has the contents of localDict. But outside the scope of "foo", dict A is empty. Meanwhile, after being assigned a key and value, dict B retains the value after coming out of function 'foo'.

How do I maintain the values that copy.deepcopy() copies outside of function "foo"?

Upvotes: 0

Views: 1756

Answers (3)

Andrew Clark
Andrew Clark

Reputation: 208505

The behavior you are seeing isn't related to deepcopy(), you are reassigning the name A to a new value, and that assignment will not carry over unless you use the global keyword. The reason the changes to B are persistent is that you are modifying a mutable variable, here are two options for how you could get the behavior you want:

  • Instead of using localDict, just modify A:

    def foo(A, B):
        A['name'] = "Simon"
        A['age'] = 55
        A['timestamp'] = "2011-05-13 15:13:22"
        A['phone'] = {'work':'555-123-1234', 'home':'555-771-2190', 'mobile':'213-601-9100'}
    
        B['me'] = 'John Doe'
        return
    
  • Use A.update(copy.deepcopy(localDict)) instead of A = copy.deepcopy(localDict):

    def foo(A, B):
        localDict = {}
        localDict['name'] = "Simon"
        localDict['age'] = 55
        localDict['timestamp'] = "2011-05-13 15:13:22"
        localDict['phone'] = {'work':'555-123-1234', 'home':'555-771-2190', 'mobile':'213-601-9100'}
    
        A.update(copy.deepcopy(localDict))
    
        B['me'] = 'John Doe'
        return
    

Upvotes: 0

Eli Bendersky
Eli Bendersky

Reputation: 273566

Ponder this:

>>> def foo(d):
...   d = {1: 2}
... 
>>> d = {3: 4}
>>> d
{3: 4}
>>> foo(d)
>>> d
{3: 4}
>>> 

Inside foo, d = {1: 2} binds some object to the name d. This name is local, it does not modify the object d used to point to. On the other hand:

>>> def bar(d):
...   d[1] = 2
... 
>>> bar(d)
>>> d
{1: 2, 3: 4}
>>> 

So this has nothing to do with your use of (deep)copy, it's just the way "variables" in Python work.

Upvotes: 1

Pedro Werneck
Pedro Werneck

Reputation: 41918

What's happening is that inside foo() you create a copy of B and assigns it to A, shadowing the empty dict you sent as an argument by reassigning a new object to the same name. Now inside the function you have a new dict called A, completely unrelated to the A outside in the global scope, and it gets garbage collected when the function ends, so actually nothing happens, only the 'me' key added to B.

If instead of:

A = copy.deepcopy(localDict)

You do something like this, it would work as you expect:

C = copy.deepcopy(localDict)

A.update(C)

But it seems like what you really want has nothing to do with the copy module and would be something like this:

def foo (A, B):
    A['name'] = "Simon"
    A['age'] = 55
    A['timestamp'] = "2011-05-13 15:13:22"
    A['phone'] = {'work':'555-123-1234', 'home':'555-771-2190', 'mobile':'213-601-9100'}

    B['me'] = 'John Doe'

Upvotes: 0

Related Questions