Pular para o conteúdo

    Capítulo 22, Básico

    Mutabilidade e identidade

    Este é o capítulo que separa quem decorou a sintaxe de quem entende o modelo de objetos de Python. Quase todo bug estranho com listas e funções nasce aqui.

    Código deste capítulo: basico/cap22_mutabilidade.py

    Nomes, objetos e id

    Lembra da etiqueta do primeiro capítulo? Cada objeto tem uma identidade (o id), e dois nomes podem apontar para o mesmo objeto. O operador == compara valores. O operador is compara identidades:

    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))
    
    Saída
    True False True
    True
    

    a e b têm o mesmo conteúdo, mas são objetos diferentes. c é só outro nome para a.

    Mutável e imutável

    Imutáveis (o objeto nunca muda)Mutáveis (o objeto pode mudar)
    int, float, bool, complexlist
    str, bytesdict
    tuple, frozensetset, bytearray
    NoneA maioria dos objetos que você define

    Quando você "altera" um imutável, na verdade cria um novo objeto e aponta o nome para ele. Quando altera um mutável, o objeto muda, e todos os nomes que apontam para ele enxergam a mudança.

    O que acontece nas funções

    O Python passa o nome do objeto, não uma cópia. Se a função altera o objeto, quem chamou vê. Se a função apenas aponta o nome local para outro objeto, quem chamou não vê:

    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)
    
    Saída
    [1, 2, 99]
    [1, 2, 99]
    

    A armadilha da matriz

    Multiplicar uma lista por um número repete a referência, não o objeto. Por isso a "matriz" abaixo tem três linhas que são, na verdade, a mesma lista:

    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)
    
    Saída
    [[1, 0, 0], [1, 0, 0], [1, 0, 0]]
    [[1, 0, 0], [0, 0, 0], [0, 0, 0]]
    

    A segunda forma usa uma compreensão de lista, que eu apresento no nível intermediário. Ela cria uma lista nova a cada volta.

    Quando usar is

    Use is com None, True e False e com valores sentinela que você mesmo criou. Para todo o resto, use ==. Comparar números ou strings com is pode funcionar por acaso, por causa de otimizações do interpretador, e quebrar em outro momento.

    Exercício 1

    Copiar uma matriz de verdade

    Escreva copiar_matriz(matriz) de modo que alterar a cópia não altere o original.