CodeGym /Cursos /Python SELF PT /Erros Padrão

Erros Padrão

Python SELF PT
Nível 20 , Lição 1
Disponível

7.1 Uso incorreto de expressões como valores padrão para argumentos de funções

Você já aprendeu muito, então vamos dar uma olhada nos erros mais comuns de iniciantes – é melhor aprender com os erros dos outros do que com os seus próprios, né?

Python permite especificar que funções podem ter argumentos opcionais atribuindo-lhes um valor padrão. Isso é uma característica muito útil da linguagem, mas pode levar a consequências indesejadas se o tipo desse valor for mutável. Por exemplo, considere a seguinte definição de função:


def foo(bar=[]):  # bar - é um argumento opcional 
# e por padrão é igual a uma lista vazia.
    bar.append("baz")  # essa linha pode causar problemas...
    return bar

Um erro comum aqui é pensar que o valor do argumento opcional será definido para o valor padrão cada vez que a função for chamada sem um valor para esse argumento.

No código acima, por exemplo, pode-se supor que chamando novamente a função foo() (ou seja, sem especificar um valor para o argumento bar), ela sempre retornará ["baz"], já que se supõe que cada vez que foo() é chamado (sem especificar o argumento bar), bar é definido para [] (ou seja, uma nova lista vazia).

Mas vamos ver o que acontece na realidade:


result = foo()
print(result)  # ["baz"]
            
result = foo()
print(result)  # ["baz", "baz"]
            
result = foo()
print(result)  # ["baz", "baz", "baz"]

Por que a função continua adicionando o valor baz à lista existente cada vez que foo() é chamada, em vez de criar uma nova lista a cada vez?

A resposta para essa pergunta é um entendimento mais profundo do que acontece em Python "por baixo do capô". Ou seja: o valor padrão para uma função é inicializado apenas uma vez, no momento da definição da função.

Assim, o argumento bar é inicializado com o valor padrão (ou seja, uma lista vazia) somente quando foo() é definido pela primeira vez, mas chamadas subsequentes para foo() (ou seja, sem especificar o argumento bar) continuarão a usar a mesma lista que foi criada para o argumento bar no momento da primeira definição da função.

Para referência, uma "solução alternativa" comum para este erro é a seguinte definição:


def foo(bar=None):             
    if bar is None:
        bar = []                                          
    bar.append("baz")   
    return bar
    
result = foo()
print(result)  # ["baz"]
    
result = foo()
print(result)  # ["baz"]
    
result = foo()
print(result)  # ["baz"]

7.2 Uso incorreto de variáveis de classe

Considere o seguinte exemplo:


class A(object):
    x = 1
       
class B(A):
    pass
       
class C(A):
    pass
       
print(A.x, B.x, C.x) 
# 1 1 1

Parece tudo certo.

Agora vamos atribuir um valor ao campo x da classe B:


B.x = 2
print(A.x, B.x, C.x)
# 1 2 1

Tudo como esperado.

Agora vamos mudar a variável de classe A:


A.x = 3
print(A.x, B.x, C.x)
# 3 2 3

Estranho, apenas mudamos A.x. Por que C.x também mudou?

Em Python, variáveis de classe são tratadas como dicionários e seguem o que muitas vezes é chamado de Ordem de Resolução de Métodos (MRO). Assim, no código acima, como o atributo x não é encontrado na classe C, ele será encontrado em suas classes base (apenas A no exemplo acima, embora Python suporte herança múltipla).

Em outras palavras, C não tem sua própria propriedade x, independente de A. Assim, referências a C.x são na verdade referências a A.x. Isso pode causar problemas se tais casos não forem devidamente levados em consideração. Ao estudar Python, preste atenção especial aos atributos de classe e como lidar com eles.

7.3 Especificação incorreta de parâmetros para o bloco de exceção

Suponha que você tenha o seguinte pedaço de código:


try:
    l = ["a", "b"]
    int(l[2])
except ValueError, IndexError:  # Para capturar ambas exceções, certo?
    pass
        
Traceback (most recent call last):
    File "<stdin>", line 3, in <module>
IndexError: list index out of range

O problema aqui é que a expressão except não aceita uma lista de exceções especificadas dessa maneira. Como resultado, no código acima a exceção IndexError não é capturada pela expressão except; em vez disso, a exceção acaba sendo vinculada a um parâmetro chamado IndexError.

Importante! Você pode encontrar tal código em exemplos na internet, pois ele era usado no Python 2.x.

A maneira correta de capturar várias exceções com uma expressão except é especificar o primeiro parâmetro como uma tupla contendo todas as exceções que você deseja capturar. Além disso, para legibilidade, você pode usar a palavra-chave as, como este exemplo:


try:
    l = ["a", "b"]
    int(l[2])
except (ValueError, IndexError) as e:  
    pass
Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION