Skip to content

    Chapter 22, Basic

    Mutability and identity

    This is the chapter that separates those who memorized the syntax from those who understand the object model of Python. Almost every strange bug with lists and functions originates here.

    Code for this chapter: basico/cap22_mutabilidade.py

    Names, objects, and id

    Remember the label from the first chapter? Each object has an identity (the id), and two names can point to the same object. The operator == compares values. The operator is compares identities:

    basico/cap22_mutabilidade.pylinhas 10 a 14
    a = [1, 2]
    b = [1, 2]
    c = a
    print(a == b, a is b, a is c)
    print(id(a) == id(c))
    
    Output
    True False True
    True
    

    a and b have the same content, but are different objects. c is just another name for a.

    Mutable and immutable

    Immutable (the object never changes)Mutable (the object can change)
    int, float, bool, complexlist
    str, bytesdict
    tuple, frozensetset, bytearray
    NoneMost of the objects you define

    When you "change" an immutable, you actually create a new object and point the name to it. When you change a mutable, the object changes, and all names pointing to it see the change.

    What happens in functions

    Python passes the name of the object, not a copy. If the function alters the object, the caller sees it. If the function only points the local name to another object, the caller does not see it:

    basico/cap22_mutabilidade.pylinhas 19 a 31
    def anexar(lista):
        lista.append(99)
    
    
    def reatribuir(lista):
        lista = [0]
    
    
    dados = [1, 2]
    anexar(dados)
    print(dados)
    reatribuir(dados)
    print(dados)
    
    Output
    [1, 2, 99]
    [1, 2, 99]
    

    The array trap

    Multiplying a list by a number repeats the reference, not the object. That's why the "matrix" below has three rows that are, in fact, the same list:

    basico/cap22_mutabilidade.pylinhas 36 a 42
    errada = [[0] * 3] * 3
    errada[0][0] = 1
    print(errada)
    
    certa = [[0] * 3 for _ in range(3)]
    certa[0][0] = 1
    print(certa)
    
    Output
    [[1, 0, 0], [1, 0, 0], [1, 0, 0]]
    [[1, 0, 0], [0, 0, 0], [0, 0, 0]]
    

    The second form uses a list comprehension, which I present at the intermediate level. It creates a new list on each iteration.

    When to use is

    Use is with None, True and False and with sentinel values that you created yourself. For everything else, use ==. Comparing numbers or strings with is may work by chance, due to optimizations of the interpreter, and break at another time.

    Exercise 1

    Copy a truth matrix

    Write copiar_matriz(matriz) so that altering the copy does not change the original.