Does Python Make a Copy of Objects on Assignment

Does Python make a copy of objects on assignment?

This is because in Python, variables (names) are just references to individual objects. When you assign dict_a = dict_b, you are really copying a memory address (or pointer, if you will) from dict_b to dict_a. There is still one instance of that dictionary.

To get the desired behavior, use either the dict.copy method, or use copy.deepcopy if your dict may have nested dicts or other nested objects.

>>> a = {1:2}
>>> b = a.copy()
>>> b
{1: 2}
>>> b[3] = 4
>>> a
{1: 2}
>>> b
{1: 2, 3: 4}
>>>

Does python copy objects on assignment?

It doesn't copy anything. It's just that assigning to self.curstack does not modify whatever self.curstack used to refer to. It just makes self.curstack refer to something else.

Think of it this way. self.curstack points to some stuff. With objs = self.curstack you make objs point to that same stuff. With self.curstack = [], you make self.curstack point to an empty list. This doesn't remove any of the stuff that self.curstack used to point at; that stuff is still there and objs is still pointing at it.

This article explains it using a nice analogy of label tags.

Does Python copy objects on instance attribute assignment?

Does Python make a copy of objects on assignment?

This is because in Python, variables (names) are just references to individual objects. When you assign dict_a = dict_b, you are really copying a memory address (or pointer, if you will) from dict_b to dict_a. There is still one instance of that dictionary.

In your example, df and instance.df is the same thing of dict_a and dict_b, which is only holding the reference of the instance.

How to delete every reference of an object in Python?

No no no. Python has a garbage collector that has very strong territory issues - it won't mess with you creating objects, you don't mess with it deleting objects.

Simply put, it can't be done, and for a good reason.

This is by design and intentional, delete the variable of df, would not eliminate the existence of the instance, it only remove the reference from you df, therefore, as long as instance.df is still holding the instance, you could still access it even you deleted the variable df.

Is copy.copy different from assignment in python

All python variables are bindings to some objects in memory.

  1. Does this code do anything?

Yes, it does.
Actually it creates a new object in memory with new name.


  1. How does copy.copy(node) differ from node = node?

By assignment you are getting different names for the same object.
So if you are really need a copy you should use shallow or deep copy.

Example:

>>> x = [1]
>>> id(x)
35964488
>>> y = x
>>> id(y)
35964488
>>> z = x[:]
>>> id(z)
35964768

So x and y have same id, while z has different.

Same here:

>>> import copy
>>> c = copy.copy(x)
>>> id(c)
35985168
>>> id(x)
35964488

Quote from docs about shallow and deep copy:

The difference between shallow and deep copying is only relevant for
compound objects (objects that contain other objects, like lists or
class instances):

A shallow copy constructs a new compound object and then (to the extent possible) inserts references into it to the objects found in
the original.

A deep copy constructs a new compound object and then, recursively, inserts copies into it of the objects found in the
original.

Shouldn't shallow copy create an object with different id in python?

In python, assignment never copies data. You just get a new name that references the same value.

There are basically three cases:

Assignment: just a new name that points to the same object.

copy.copy the object itself is copied, but members of the object are not. So a shallow copy of a list will be a new list, have a new ID but will point to the same objects in memory for its elements as the original list.

copy.deepcopy recursively copies everything.

Good resources:
http://www.pythontutor.com/visualize.html#mode=edit

Ned Batchelder, Facts and myths about python names and vague https://youtu.be/_AEJHKGk9ns

Is it true that Python never implicitly copies objects ?

The statement in the linked answer is broader than it should be. Implicit copies are rare in Python, and in the cases where they happen, it is arguable whether Python is performing the implicit copy, but they happen.

What is definitely true is that the default rules of name assignment do not involve a copy. By default,

a = b

will not copy the object being assigned to a. This default can be overridden by a custom local namespace object, which can happen when using exec or a metaclass with a __prepare__ method, but doing so is extremely rare.

As for cases where implicit copies do happen, the first that comes to mind is that the multiprocessing standard library module performs implicit copies all over the place, which is one of the reasons that multiprocessing causes a lot of confusion. Assignments other than name assignment may also involve copies; a.b = c, a[b] = c, and a[b:c] = d may all involve copies, depending on what a is. a[b:c] = d is particularly likely to involve copying d's data, although it will usually not involve producing an object that is a copy of d.

python: changes to my copy variable affect the original variable

That is because in python setting a variable actually sets a reference to the variable. Almost every person learning python encounters this at some point. The solution is simply to copy the list:

copy_list = org_list[:] 

Is Copying for List Assignments acceptable?

There is nothing inherently wrong with this. As long as your class is well documented, if you have a good reason to do this go for it. Consider the following class from the standard library shelve module, which essentially provides a dict-like database interface. Since using object[key] = value will write to disk, and retrieving that object will retrieve it from disk, it won't give you the same object (necessarily, there are caching options)! So:

In [1]: import shelve

In [2]: data = [[1,2,3],[4,5,6]]

In [3]: database = shelve.open('db')

In [4]: database['key'] = data[0]

In [5]: database['key']
Out[5]: [1, 2, 3]

In [6]: database['key'] is data[0]
Out[6]: False

That part of the documentation is referring to assignment statements to a variable, like this:

some_var = foo

Where indeed, it never copies. And you can't really change that (well, maybe, but that's another question). You are free to implement __setitem__ and __getitem__ however you wish.

How do I clone a list so that it doesn't change unexpectedly after assignment?

new_list = my_list doesn't actually create a second list. The assignment just copies the reference to the list, not the actual list, so both new_list and my_list refer to the same list after the assignment.

To actually copy the list, you have several options:

  • You can use the builtin list.copy() method (available since Python 3.3):

    new_list = old_list.copy()
  • You can slice it:

    new_list = old_list[:]

    Alex Martelli's opinion (at least back in 2007) about this is, that it is a weird syntax and it does not make sense to use it ever. ;) (In his opinion, the next one is more readable).

  • You can use the built in list() constructor:

    new_list = list(old_list)
  • You can use generic copy.copy():

    import copy
    new_list = copy.copy(old_list)

    This is a little slower than list() because it has to find out the datatype of old_list first.

  • If you need to copy the elements of the list as well, use generic copy.deepcopy():

    import copy
    new_list = copy.deepcopy(old_list)

    Obviously the slowest and most memory-needing method, but sometimes unavoidable. This operates recursively; it will handle any number of levels of nested lists (or other containers).

Example:

import copy

class Foo(object):
def __init__(self, val):
self.val = val

def __repr__(self):
return f'Foo({self.val!r})'

foo = Foo(1)

a = ['foo', foo]
b = a.copy()
c = a[:]
d = list(a)
e = copy.copy(a)
f = copy.deepcopy(a)

# edit orignal list and instance
a.append('baz')
foo.val = 5

print(f'original: {a}\nlist.copy(): {b}\nslice: {c}\nlist(): {d}\ncopy: {e}\ndeepcopy: {f}')

Result:

original: ['foo', Foo(5), 'baz']
list.copy(): ['foo', Foo(5)]
slice: ['foo', Foo(5)]
list(): ['foo', Foo(5)]
copy: ['foo', Foo(5)]
deepcopy: ['foo', Foo(1)]


Related Topics



Leave a reply



Submit