19 minutos de leitura

Os principais erros de iniciantes em Python

Por Juliano Faccioni
Conteúdos do artigo

Descubra quais são os erros mais comuns em Python para iniciantes e aprenda a contorná-los.

Python é uma linguagem de programação muito procurada por iniciantes. Embora seja mais simples e intuitiva do que outras linguagens, ainda há “armadilhas” em seu uso, principalmente para quem está começando.

Neste artigo, exploraremos os principais erros que os iniciantes em Python cometem. Além disso, vamos entender por que cada erro ocorre e qual é a melhor forma de solucioná-los. Então, se você ainda não tem conhecimento suficiente para acompanhar este conteúdo, recomendamos o curso Python Starter, disponível em qualquer uma das trilhas da Asimov Academy. Caso contrário, vamos em frente!

Erro #1: Confundir strings e variáveis

Um erro extremamente comum para quem está começando em Python é confundir o que é uma string e o que é uma variável. Ambos aparecem no script como texto, mas são conceitualmente diferentes.

Uma string é um tipo de dado em Python, uma sequência de caracteres que representa algum tipo de informação textual. 'Olá Mundo!', "Juliano", 'RJ95-K' e "Análise de Dados com Python" são exemplos de strings.

Já uma variável é simplesmente um nome atribuído a algum dado. Em programação, usamos variáveis porque é mais fácil usar nomes para identificar valores em vez de usarmos os valores diretamente.

Observe a comparação abaixo, na qual calculamos a área de um círculo com raio 5:

# sem variaveis
area_circulo = 3.1415 * (5 ** 2)

# com variaveis
raio = 5
pi = 3.1415
area_circulo = pi * (raio ** 2)

Perceba como, no segundo exemplo, é mais fácil entender o que o código está fazendo, pois utilizamos nomes (variáveis) para os valores do raio e de pi.

Obviamente, também é possível atribuir uma variável a uma string. O código abaixo exibe o valor "Juliano", que está guardado dentro da variável nome:

nome = "Juliano"
print(nome)

Para diferenciar uma string de uma variável, baseie-se nos seguintes pontos:

  • Strings são valores, e o valor de uma string é uma sequência de caracteres.
  • Variáveis são nomes que podem ser atribuídos a um valor qualquer (inclusive a uma string).
  • Strings são sempre escritas com aspas duplas ou simples. Portanto, não use aspas para acessar uma variável.
  • Numa atribuição de valor com o operador  = , variáveis ficam sempre à esquerda, enquanto strings ficam à direita.

Lembre-se dessa explicação na próxima vez em que tentar acessar o valor da variável nome usando a string "nome"!

Erro #2: Sobrescrever nomes de funções e objetos

Em Python, existem diversas funções embutidas (built-in functions) que estão disponíveis para uso a qualquer momento. Alguns exemplos são as funções print(), input(), list() e id(). A documentação do Python apresenta a lista completa (em inglês).

O erro ocorre quando sobrescrevemos uma dessas funções com uma variável nossa. No exemplo abaixo, criamos uma variável chamada list, que guarda uma lista de valores qualquer. O problema é que esse nome de variável sobrescreve a função embutida list():

list()  # não dá erro

list = [1, 2, 4, 7, 3]

list()  # TypeError: 'list' object is not callable

Inicialmente, nenhum erro é gerado ao chamarmos list() (esta chamada apenas cria uma lista vazia, descartada em seguida). Contudo, após sobrescrevermos a função e executarmos list() novamente, um erro é gerado. Efetivamente, perdemos a referência à função original!

Para evitar a sobrescrita de uma função embutida, a dica é usar um editor de texto com destaque para a sintaxe de Python, como o VS Code. Nesse caso, ao digitar a palavra list, a cor do texto muda, indicando que é um nome “reservado” pelo Python.

Erro #3: Não entender a diferença entre print e return

Este erro em Python é extremamente comum entre iniciantes na linguagem. Muitas vezes, eles não entendem qual é a utilidade de “dar print” em algumas linhas ou o que significa de fato retornar um valor.

Em poucas palavras, “printar” significa exibir um valor na tela, enquanto “retornar” refere-se a receber um valor que se pode acessar mais tarde, em outras partes do código.

Vamos usar um exemplo para entender melhor.

Imagine que a função conta_complexa() é uma operação bastante demorada. Se quisermos apenas exibir o resultado dessa operação, podemos usar a função print():

print(conta_complexa())

Note que o código acima apenas exibe o resultado uma única vez. E se quisermos ver o resultado novamente? E se precisarmos fazer uma segunda operação com o resultado? Nesses casos, é necessário chamar a função conta_complexa() uma nova vez, o que ocupa novamente todo o tempo de operação.

Por outro lado, se quisermos realizar operações com o resultado, precisamos armazenar o retorno da função conta_complexa() em uma variável:

resultado = conta_complexa()
print(resultado)

resultado_dividido = resultado / 2
print(resultado_dividido)

# ...

Nesse exemplo, salvamos o retorno da função na variável resultado. A partir desse momento, temos a liberdade de usar o valor da forma como quisermos: podemos realizar novas operações matemáticas ou exibi-lo quantas vezes quisermos com print(). Tudo isso sem precisar executar a função conta_complexa() uma segunda vez!

Notebooks do Jupyter x shell de IPython

A confusão entre print e return vem principalmente do uso de ferramentas como Notebooks do Jupyter ou shell de IPython. Tais ferramentas são excelentes para iniciantes testarem a funcionalidade da linguagem, mas podem confundir por apresentarem um funcionamento diferente de um script tradicional de Python.

Um shell de Python funciona em um loop REPL (do inglês read-evaluate-print loop). Isso tem uma implicação importante: todo valor retornado e que não é salvo em uma variável é printado automaticamente ao console. Essa funcionalidade é bastante prática, mas também é a fonte principal da confusão.

Veja o exemplo abaixo, executado em um shell de IPython:

Exemplo de código escrito em shell de IPython
Exemplo de código escrito em shell de IPython
  • Em 1) e 2), atribuímos valores às variáveis x e y. Nada aparece na linha de output, pois todo retorno foi devidamente “capturado” por uma variável.
  • Em 3), realizamos a operação x + y. O valor resultante foi retornado, mas não foi “capturado” por nenhuma variável. Logo, o IPython decide exibir o valor retornado na linha de output.
    • O prefixo Out[3], em vermelho, indica que algum valor foi retornado pela expressão inserida na linha de input diretamente acima(In[3]).
  • Em 4), realizamos a mesma operação, porém a entregamos para a função print. Como explicado anteriormente, a função print exibe o valor entregue a ela, porém não o retorna. (Na realidade, a função print retorna sempre o valor None, que representa a ausência de retorno.)
    • Podemos confirmar isso se escrevermos resultado = print(x + y). Nesse caso, a variável resultado terá o valor None.
    • O valor 5, resultante da operação x + y, foi apenas exibido, mas não foi retornado. Isso é indicado pela falta de prefixo Out na linha em que o 5 está exibido.

A imagem abaixo mostra a mesma situação dentro de um Notebook do Jupyter. Veja que, apesar da aparência diferente do Notebook, a mesma situação acontece:

Código escrito em um Notebook do Jupyter.
Mesmo exemplo do código anterior escrito em um Notebook do Jupyter

Erro #4: Modificar uma lista enquanto itera sobre ela

Este é um erro clássico em programação e acontece quando você tenta modificar uma lista (ou qualquer outra sequência) enquanto está iterando sobre ela.

Para exemplificar, digamos que você deseja filtrar números maiores que 100 de uma lista de valores qualquer. Inicialmente, você pode pensar em fazer algo como o código abaixo:

valores = [300, 10, 90, 150, 120, 40]
for i in range(len(valores)):
    valor = valores[i]
    if valor > 100:
        del valores[i]

print(valores)
# ERRO: IndexError: list index out of range

Como podemos ver, o código não funcionou, gerando um IndexError durante sua execução. Mas o que aconteceu exatamente?

Na linha del valores[i] dentro do loop, elementos da lista valores são deletados. Com isso, o tamanho da lista diminui ao longo das iterações.

Contudo, como iniciamos a iteração escrevendo for i in range(len(valores)), a variável i continua a iterar até o tamanho original da lista, antes de ter seus elementos deletados. Isso significa que, eventualmente, a linha valor = valores[i] acessa valores além do número de elementos da lista, produzindo o IndexError.

Lembre-se: nunca modifique um objeto enquanto você itera sobre ele!

A forma correta de filtrar elementos de uma lista é inicializar uma nova lista vazia e adicionar elementos a ela, conforme a lógica de filtro. No nosso exemplo com a lista valores, o filtro pode ser feito da seguinte forma:

valores = [300, 10, 90, 150, 120, 40]
valores_filtrados = []
for i in range(len(valores)):
    valor = valores[i]
    if valor <= 100:
        valores_filtrados.append(valor)

print(valores_filtrados)
# output: [10, 90, 40]

Note que a comparação na linha if valor <= 100: agora é o inverso da anterior; nesse caso, queremos adicionar valores à lista de valores filtrados somente se eles forem iguais ou menores a 100.

Erro #5: Usar a função type para identificar o tipo de um dado

Ao aprendermos sobre tipos de dados, é muito comum nos depararmos com a função type(), a qual retorna o tipo de dado do argumento que recebe. Por exemplo:

print(type('string_qualquer'))
# output: <class 'str'>

print(type(100))
# output: <class int'>

print(type(False))
# output: <class 'bool'>

Como podemos perceber, o valor exibido embaixo é sempre o tipo de dado correspondente ao argumento passado para a função type().

O problema ocorre quando type() é usado para checar o tipo de dado de uma certa variável e, a partir disso, definir a lógica do seu programa. Algo como:

# Não faça isso!

if type(valor) == str:
    # lidar com string
elif type(valor) == float:
    # lidar com float
# etc...

Mas, afinal, qual é o problema com o código acima?

Embora ele funcione para casos simples, a função type() possui algumas limitações. No lugar dela, prefira sempre usar a função isinstance() para checar o tipo de um dado.

if isinstance(valor, str):
    # lidar com string
elif isinstance(valor, float):
    # lidar com float
# etc...

Por exemplo, a função type() não consegue lidar de forma apropriada com herança de classes (caso você não saiba o que é uma classe, temos um artigo sobre os conceitos de programação orientada a objetos).

Se criarmos nossa própria subclasse de str, por exemplo, apenas a função isinstance() a considera como sendo uma instância de str de fato:

class MinhaString(str):
    pass

s = MinhaString('abc')

print(type(s) == str)
# output: False

print(isinstance(s, str))
# output: True

Além disso, a função isinstance() permite checar múltiplos tipos de dados ao mesmo tempo. Se quisermos conferir se um certo dado é um int ou float, por exemplo, podemos fazer facilmente da seguinte forma:

valor = 10

if isinstance(valor, (int, float)):
    print('Valor é numero')

# output: Valor é numero

Essa função é tão útil que está na nossa lista de 15 hacks básicos de Python para iniciantes.

Erro #6: Não utilizar funções no seu script

Quando começamos a aprender sobre Python ou quando precisamos apenas escrever um script rápido e curto, é comum simplesmente “largarmos” as linhas de código sem prestar muita atenção em sua organização.

Contudo, quando nosso código começa a evoluir, é natural organizarmos diferentes porções lógicas em scripts separados. É possível importar a funcionalidade de um script a partir de outro usando a palavra-chave import. Nesse momento, muitos iniciantes ficam confusos quando o código acaba sendo executado ao ser importado.

Imagine, por exemplo, que você possui dois scripts: valores.py e analise.py. Eles possuem o seguinte código:

valores.py

x = 10
print("O valor é", x)

analise.py

from valores import x

y = x / 2
print("O resultado da análise é", y)

O que acontece se executarmos o script analise.py? Vejamos:

Resultado da execução do script analise.py
Resultado da execução do script analise.py

A mensagem contida no script valores.py foi exibida, apesar de não termos importado nada relacionado a ela! Por que isso aconteceu?

Quando importamos um script, todo o código existente nele é lido e executado. É por isso que o resultado da linha print("O valor é", x) aparece no terminal, por mais que tivéssemos interesse apenas na variável x.

O que aconteceria se o script desorganizado contivesse funções demoradas, que realizam um cálculo pesado ou criam conexões com bancos de dados? Nesse caso, mesmo se quiséssemos apenas importar uma variável, teríamos de esperar o tempo de execução de tais processos não relacionados.

A solução é simples: precisamos reestruturar o código de valores.py em funções, para que elas não sejam executadas diretamente ao importar o código. No lugar disso, elas serão computadas apenas quando forem explicitamente executadas.

Abaixo, apresentamos uma ideia de reestruturação simples:

valores.py

def pegar_x():
    x = 10
    return x

def exibir_x():
    x = pegar_x()
    print("O valor é", x)

analise.py

from valores import pegar_x

x = pegar_x()
y = x / 2
print("O resultado da análise é", y)

Executando analise.py novamente no terminal, temos:

Resultado da execução do script analise.py após modificações
Resultado da execução do script analise.py após modificações.

Sucesso! A mensagem “o valor é 10”, que movemos para dentro da função exibir_x(), não apareceu no output. Faz sentido: a função não foi executada em nenhum momento.

Esse é um exemplo simples, mas que demonstra claramente a diferença de ter um script organizado em funções. Além de impedir que algum código desnecessário se execute na importação, criar funções ajuda a limpar e organizar o próprio código e também a poupar tempo do desenvolvedor.

Aprenda mais sobre Python

Neste artigo, visitamos alguns dos erros mais comuns que os iniciantes encontram em Python. Apesar de os conceitos discutidos serem simples, eles são a fonte de grandes dores de cabeça para quem é iniciante em Python.

Se você pretende se aprofundar em Python e programar como um profissional, recomendamos nossa Trilha Python Office. Nela, você aprenderá Python do zero e será capaz de montar diversas automações com e-mails, arquivos de Excel e PDFs, além de web scraping.

Inscreva-se gratuitamente e fique atualizado

Receba toda semana um resumo dos principais conteúdos da Asimov direto no seu e-mail. 100% livre de spam.

Áreas de interesse:
Conteúdos do tutorial