Aliasing vs Copying

  • As assignments are concerned, there is a difference between two types of data:

    • Simple data types: string, integer, float, Boolean, tuple

    • Containers (and other complex data): Images, lists, sets and dictionaries

      We will only look at lists in this document.

  • When you make an assignment for simple data types, their value is copied to the assigned variable.

    >>> x  = 4
    >>> y = x
    >>> x = 5
    >>> x
    5
    >>> y
    4
    

    Copy the value 4 to variable y.

  • When you make an assignment to a list, a copy of the list is not created. Because, lists can be huge. So, we want to control when we create copies.

    >>> animals
    ['dog', 'cat', 'pig']
    >>> animals_alias = animals
    >>> animals_alias
    ['dog', 'cat', 'pig']
    

    This means, animals_alias is an alias to the animals list. They are just two different names for the same variable.

    If you change the list through one alias, you also change it through the other one.

    >>> animals.append("cow")
    >>> animals
    ['dog', 'cat', 'pig', 'cow']
    >>> animals_alias
    ['dog', 'cat', 'pig', 'cow']
    
  • This is true for any list. Let us look at list of lists:

    >>> names = [ ['Sun', 500], ['Moon', 200] ]
    >>>
    >>> name1 = names[0]
    >>> name1
    ['Sun', 500]
    >>> name1[1] = 600
    >>> name1
    ['Sun', 600]
    >>> names
    [['Sun', 600], ['Moon', 200]]
    

    In this example, since names[0] is also a list, the variable name1 becomes an alias to this list.

  • Now, when you have list of lists, be careful. The copy functions will only copy the top level elements, which is why we keep calling it a shallow copy.

    >>> stuff = ['a', 1, ['bob',5] ]
    >>>
    >>> stuff_copy = list(stuff)
    

    stuff_copy is a copy of values from stuff, so a copy of ‘a’ and 1, and an alias to the final list. We do not copy lists by default. See in action:

    >>> stuff.append('e')
    >>> stuff
    ['a', 1, ['bob', 5], 'e']
    >>> stuff_copy
    ['a', 1, ['bob', 5]]
    >>> stuff[2].append('nooooo')
    >>> stuff
    ['a', 1, ['bob', 5, 'nooooo'], 'e']
    >>> stuff_copy
    ['a', 1, ['bob', 5, 'nooooo']]
    

    stuff and stuff_copy are different lists, but the item at index 2 is a list and they both point to the same list. This is true for all the other operations we learnt: replication, concatenation and slicing.

  • The same principle extends to functions. Whenever we pass a variable to a function as an argument, if it is a container, then the variable is an alias to the list. We will go through this in more detail in the function review.

  • Yes, there is a deepcopy function in a module called copy to resolve this. For now, it is enough to be aware of this.