Curso
A demanda por habilidades em programação Python tem crescido nos últimos anos. Pra te ajudar a melhorar suas habilidades de programação em Python, a gente selecionou 30 truques legais que você pode usar pra deixar seu código ainda melhor. Tente aprender uma por dia durante os próximos 30 dias e confira nossa publicação sobre as melhores práticas do Python para garantir que seu código seja o melhor da categoria.
Se suas habilidades em Python não estão à altura, você também pode aprimorá-las com nosso programa Python Skill.
#1 Fatiar
a = "Hello World!"
print(a[::-1])
"""
!dlroW olleH
"""
O slicing é um recurso do Python que usa indexação pra permitir que os usuários acessem um subconjunto de uma sequência. Um índice é só a posição de um elemento numa sequência. Se o tipo de sequência for mutável, você pode usar o corte para extrair e modificar dados.
Observação: Também podemos usar o corte em uma sequência imutável, mas tentar modificar o corte vai gerar um TypeError.
O formato em que as fatias são implementadas é: sequência[início:fim:intervalo]. Se nenhum valor for especificado nos parâmetros start, stop e step, a sequência vai usar os padrões. Os padrões são:
- O padrão para “iniciar” é 0.
- “stop” tem como padrão o comprimento da sequência
- O “step” fica em 1 por padrão, se não for especificado.
Quando você passa uma sequência [início:fim], os elementos que você recebe vão do índice inicial até o índice fim-1(o índice fim não está incluído).
Também podemos passar índices negativos, que podem ser usados para inverter a sequência. Por exemplo, numa lista de 4 elementos, o índice 0 também é o índice -4, e o último índice também é -1. No código de exemplo acima, esse conhecimento foi aplicado ao parâmetro step da sequência. Então, a string foi impressa de trás para frente, começando do fim da sequência até o índice 0.
#2 Troca no local / Atribuição simultânea
a = 10
b = 5
print(f"First: {a, b}")
"""
First: (10, 5)
"""
a, b = b, a + 2
print(f"Second: {a, b}")
"""
Second: (5, 12)
"""
Se você achou que o valor de b seria 7 em vez de 12, você caiu na armadilha da troca no local.
Em Python, a gente pode descompactar iteráveis para variáveis em uma única atribuição usando a descompactação automática. Por exemplo:
a, b, c = [1, 2, 3]
print(a)
print(b)
print(c)
"""
1
2
3
"""
Também podemos juntar vários valores em uma única variável usando * – esse truque do Python é chamado de empacotamento. Abaixo está um exemplo de embalagem.
a, *b = 1, 2, 3
print(a, b)
"""
1 [2, 3]
"""
Combinar o empacotamento e o desempacotamento automáticos cria uma técnica chamada atribuição simultânea. A gente pode usar a atribuição simultânea pra atribuir uma série de valores a uma série de variáveis.
#3 Lista vs. Tuplas
import sys
a = [1, 2, 3, 4, 5]
b = (1, 2, 3, 4, 5)
print(f"List size: {sys.getsizeof(a)} bytes")
print(f"Tuple size: {sys.getsizeof(b)} bytes")
"""
List size: 52 bytes
Tuple size: 40 bytes
"""
A maioria dos programadores Python conhece a estrutura de dados lista. Não dá pra dizer o mesmo das tuplas. Ambos são iteráveis, permitem indexação e permitem o armazenamento de tipos de dados heterogêneos. Mas tem situações em que usar uma tupla pode ser melhor do que uma lista.
Primeiro, as listas são mutáveis, o que significa que podemos modificá-las como quisermos:
a = [1,2,3,4,5]
a[2] = 8
print(a)
"""
[1,2,8,4,5]
"""
Tuplas, por outro lado, são imutáveis, o que significa que tentar modificá-las vai gerar um erro TypeError.
Por isso, as tuplas são mais eficientes em termos de memória, já que o Python consegue alocar o bloco de memória certo que os dados precisam. Já numa lista, a gente precisa reservar memória extra, só por precaução, caso a gente precise aumentar ela — isso é chamado de alocação dinâmica de memória.
TLDR; Quando você não quer que os dados sejam alterados, é melhor usar uma estrutura de dados tupla em vez de uma lista, por causa da memória. As tuplas também são mais rápidas do que as listas.
Saiba mais sobre estruturas de dados Python neste tutorial.
#4 Geradores
a = [x * 2 for x in range(10)]
b = (x * 2 for x in range(10))
print(a)
print(b)
"""
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
<generator object <genexpr> at 0x7f61f8808b50>
"""
Compreensões de lista são a maneira Python de criar uma lista a partir de outro iterável - é muito mais rápido do que usar um loop for. Mas e se você, sem querer, trocar os colchetes [] por parênteses ()? Você recebe um objeto gerador.
Em Python, os colchetes com lógica de compreensão de lista criam o que é conhecido como objeto gerador. Geradores são um tipo especial de iterável. Ao contrário das listas, elas não guardam seus itens. Em vez disso, eles guardam instruções para gerar cada elemento em ordem e o estado atual das iterações.
Cada elemento só é gerado quando solicitado, usando uma técnica chamada avaliação preguiçosa. A principal vantagem dessa dica Python usando um gerador é que ela usa menos memória, já que a sequência inteira não é construída de uma só vez.
#5 Aliasing
a = [1, 2, 3, 4 ,5]
b = a
# Change the 4th index in b
b[4] = 7
print(id(a))
print(id(b))
print(a) # Remember we did not explicitly make changes to a.
"""
15136008
15136008
[1, 2, 3, 4, 7]
"""
Python é uma linguagem de programação orientada a objetos — tudo é um objeto. Então, atribuir um objeto a um identificador é criar uma referência para o objeto.
Quando atribuímos um identificador a outro identificador, acabamos com dois identificadores que fazem referência ao mesmo objeto. Esse é um conceito conhecido como aliasing. Alterações em um alias afetarão o outro. Às vezes, esse comportamento é bem-vindo, mas, muitas vezes, nos pega de surpresa.
Uma maneira de contornar isso é evitar o aliasing ao usar objetos mutáveis. Outra solução poderia ser criar um clone do objeto original em vez de uma referência.
A maneira mais fácil de criar um clone é usar o recurso de divisão:
b = a[:]
Isso vai criar uma nova referência a um objeto de lista no identificador b.
Você poderia pensar em várias outras soluções, como chamar a lista(a) ao atribuir os dados a outro identificador e usar o método copy().
#6 O operador “não”
a = []
print(not a)
"""
True
"""
Nossa próxima dica Python é a maneira mais fácil de verificar se sua estrutura de dados está vazia usando o operador not. Python tem um recurso chamado not é um operador lógico que retorna True se a expressão não for verdadeira, ou então retorna False – ele inverte o valor de verdade de expressões e objetos booleanos.
Outra forma de ver isso sendo usado é em uma instrução if:
if not a:
# do something...
Quando a é Verdadeiro então o não vai devolver Falsee vice-versa.
É complicado entender, então dá uma chance.
#7 Cordas F
first_name = "John"
age = 19
print(f"Hi, I'm {first_name} and I'm {age} years old!")
"""
Hi, I'm John and I'm 19 years old!
"""
Às vezes, a gente pode precisar formatar um objeto string; o Python 3.6 trouxe um recurso legal chamado f-strings pra simplificar esse processo. É legal entender como as strings eram formatadas antes do novo lançamento pra curtir melhor o novo método.
Veja como as strings costumavam ser formatadas:
first_name = "John"
age = 19
print("Hi, I'm {} and I'm {} years old!".format(first_name, age))
"""
Hi, I'm John and I'm 19 years old!
"""
Basicamente, a nova forma de formatação é mais rápida, mais fácil de ler, mais concisa e mais difícil de errar.
Outra utilidade das f-strings é imprimir um nome identificador junto com o valor. Isso foi introduzido no Python 3.8.
x = 10
y = 20
print(f"{x = }, {y = }")
"""
x = 10, y = 20
"""
Dá uma olhada nesse tutorial sobre formatação de strings F em Python pra saber mais.
#8 O parâmetro “end” das funções de impressão
languages = ["english", "french", "spanish", "german", "twi"]
print(' '.join(languages))
"""
english french spanish german twi
"""
É bem comum usar um imprimir sem definir nenhum dos seus parâmetros opcionais. Por isso, vários Pythonistas não sabem que dá pra controlar a saída até certo ponto.
Um parâmetro opcional que podemos alterar é o end. O parâmetro end diz o que deve aparecer no final de uma chamada para um imprimir .
O padrão para o fim é “\n”, que diz ao Python para começar uma nova linha. No código acima, a gente mudou para espaço. Então, o resultado mostra todos os elementos da nossa lista na mesma linha.
#9 Anexar à tupla
a = (1, 2, [1, 2, 3])
a[2].append(4)
print(a)
"""
(1, 2, [1, 2, 3, 4])
"""
Já sabemos que tuplas são imutáveis – veja o truque Python #3 Lista vs. Tuplas. Tentar mudar o estado de uma tupla vai gerar um erro TypeError. Mas, se você pensar em um objeto tupla como uma sequência de nomes com ligações a objetos que não podem ser alterados, você pode ver as coisas de maneira diferente.
Os dois primeiros elementos da nossa tupla são números inteiros - eles são imutáveis. O último elemento da nossa tupla é uma lista, um objeto mutável em Python.
Se a gente pensar na nossa lista como só mais um nome numa sequência ligada a um objeto que não dá pra mudar, a gente vai perceber que a lista ainda pode ser modificada de dentro da tupla.
A gente recomendaria que você fizesse isso na prática? Provavelmente não, mas é uma daquelas coisas que é bom saber!
#10 Mesclando dicionários
a = {"a": 1, "b": 2}
b = {"c": 3, "d": 4}
a_and_b = a | b
print(a_and_b)
"""
{"a": 1, "b": 2, "c": 3, "d": 4}
"""
No Python 3.9 e versões posteriores, dá pra juntar dicionários usando | (OR bit a bit). Não tem muito mais o que falar sobre esse truque específico do Python, além de que é uma solução bem mais fácil de entender!
#11 Operador ternário / Expressões condicionais
condition = True
name = "John" if condition else "Doe"
print(name)
"""
John
"""
No código acima, você pode ver o que é conhecido como operador ternário - ele também é chamado de expressão condicional entre nomes. Usamos operadores ternários para avaliar coisas com base em uma condição ser verdadeira ou falsa. Verdadeira ou Falsa.
Outra forma de escrever o nosso código acima seria assim:
condition = True
if condition:
name = "John"
else:
name = "Doe"
print(name)
"""
John
"""
Embora os dois conjuntos de código tenham o mesmo resultado, repara como a condição ternária nos permite escrever um código bem mais curto e claro. É o que os Pythonistas chamariam de maneira mais “Python” de escrever código.
#12 Remova duplicatas das listas
a = [1, 1, 2, 3, 4, 5, 5, 5, 6, 7, 2, 2]
print(list(set(a)))
"""
[1, 2, 3, 4, 5, 6, 7]
"""
A maneira mais fácil de tirar elementos duplicados de uma lista é transformar a lista em um conjunto (e depois voltar a lista, se quiser).
Por causa da mutabilidade, conjuntos e listas são bem parecidos no Python. Podemos adicionar e remover elementos de ambas as estruturas de dados à vontade, mas elas ainda são bem diferentes.
As listas são ordenadas, indexadas a partir de zero e mutáveis. Os conjuntos não têm ordem nem índice. Os elementos de um conjunto precisam ser de um tipo imutável, mesmo que o conjunto em si seja mutável — tentar pegar um elemento por meio de um índice ou modificar um elemento vai gerar um erro.
Outra diferença importante entre conjuntos e listas é que os conjuntos não podem ter itens duplicados. Foi isso que nos ajudou a tirar os elementos duplicados da nossa lista.
#13 Sublinhado isolado
>>> print(_)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
>>> 1 + 2
3
>>> print(_)
3
O sublinhado (_) é um identificador legal em Python, então dá pra usar ele pra referenciar um objeto. Mas o underscore também tem outra função: guardar o resultado da última avaliação.
A documentação diz que “o interpretador interativo deixa o resultado da última avaliação disponível na variável _. (Está guardado no módulo builtins, junto com funções integradas como print).
Como não atribuímos um objeto ao underscore antes de chamá-lo na primeira linha, recebemos um erro. Mas, quando a gente calculou o resultado de 1 + 2, o interpretador interativo guardou o resultado no identificador _ pra gente.
#14 Sublinhado para ignorar valores
for _ in range(100):
print("The index doesn't matter")
"""
The index doesn't matter
The index doesn't matter
...
"""
Na dica Python nº 13, descobrimos que o interpretador interativo deixa o último resultado de uma avaliação disponível no identificador sublinhado (_), mas esse não é o único jeito de usá-lo.
Também podemos usá-lo para representar objetos que não nos interessam ou que não usaríamos mais tarde no programa. Isso é importante porque usar um identificador em vez de um sublinhado (_) vai gerar um erro F841 quando tentarmos fazer a verificação do nosso programa. Um erro F841 só quer dizer que um nome de variável local foi atribuído, mas não foi usado no programa, o que não é legal.
#15 Traços de sublinhado
list_ = [0, 1, 2, 3, 4]
global_ = "Hi there"
Continuando com as duas dicas anteriores, o uso do sublinhado (_) no Python tem outra finalidade: evitar conflitos com palavras-chave do Python.
O PEP 8 fala que um sublinhado (_) no final deve ser “usado por convenção para evitar conflitos com palavras-chave do Python”. Também diz que “geralmente é melhor colocar um único sublinhado no final do que usar uma abreviação ou alterar a grafia”. Então, list_ é melhor que lst.
#16 Sublinhados principais
class Example:
def __init__(self):
self._internal = 2
self.external = 20
Você vai ver que os programadores Python experientes costumam colocar um sublinhado antes de um identificador ou nome de método — e por um bom motivo.
O sublinhado antes de um identificador ou método tem um significado escondido: essa variável ou método é só pra uso interno. Basicamente, é um aviso para outros programadores que foram definidos no PEP 8, mas não é obrigatório no Python. Então, os traços de destaque são um indicador fraco.
Diferente do Java, o Python não faz muita diferença entre variáveis privadas e públicas. Em outras palavras, só faz sentido porque a galera do Python concordou que faz sentido. A inclusão deles não afeta o comportamento dos seus programas.
#17 Sublinhado Visual
Aqui vai a última dica sobre sublinhados; até agora, falamos sobre três casos diferentes de uso do sublinhado, mas você pode conferir nosso tutorial para saber mais sobre o papel do sublinhado (_) em Python.
number = 1_500_000
print(number)
"""
15000000
"""
Outra maneira de usar o sublinhado é como separador visual para agrupar dígitos em literais inteiros, de ponto flutuante e números complexos – isso foi introduzido no Python 3.6.
A ideia era ajudar na legibilidade de literais longos, ou literais cujo valor deveria ser claramente separado em partes – você pode ler mais sobre isso na PEP 515.
#18 __name__ == “__main__”
if __name__ == "__main__":
print("Read on to understand what is going on when you do this.")
"""
print("Read on to understand what is going on when you do this.")
"""
É bem provável que você já tenha visto essa sintaxe em vários programas Python; o Python usa um nome especial chamado “__main__” e define-o como um identificador chamado __name__ se o arquivo Python que está sendo executado for o programa principal.
Se decidirmos importar o módulo mostrado na captura de tela para outro módulo (arquivo Python) e executarmos esse arquivo, a verdade da expressão em nosso código será falsa. Isso acontece porque, quando importamos de outro módulo, o identificador __name__ é definido como o nome do módulo (arquivo Python).
#19 O método “setdefault”
import pprint
text = "It's the first of April. It's still cold in the UK. But I'm going to the museum so it should be a wonderful day"
counts = {}
for word in text.split():
counts.setdefault(word, 0)
counts[word] += 1
pprint.pprint(counts)
"""
{'April.': 1,
'But': 1,
"I'm": 1,
"It's": 2,
'UK.': 1,
'a': 1,
'be': 1,
'cold': 1,
'day': 1,
'first': 1,
'going': 1,
'in': 1,
'it': 1,
'museum': 1,
'of': 1,
'should': 1,
'so': 1,
'still': 1,
'the': 3,
'to': 1,
'wonderful': 1}
"""
Você pode querer definir um valor para várias chaves em um dicionário. Por exemplo, quando você está programando as contagens de palavras em um corpus. A maneira mais comum de fazer isso é assim:
- Verifique se a chave existe no dicionário
- Se sim, aumente o valor em 1.
- Se não estiver, adicione-o e defina o valor como 1.
É assim que fica no código:
counts = {}
for word in text.split():
if word in counts:
counts[word] += 1
else:
counts[word] = 1
Uma maneira mais direta de fazer isso é usar o método setdefault() no seu objeto dicionário.
O primeiro argumento passado para o método é a chave que queremos verificar. O segundo argumento passado é o valor a ser definido para a chave, caso ela ainda não exista no dicionário. Se a chave existir, o método retornará o valor da chave. Então, não seria alterado.
#20 Correspondência de expressão regular
import re
number = re.compile(r"(0)?(\+44)?\d{10}")
num_1 = number.search("My number is +447999999999")
num_2 = number.search("My number is 07999999999")
print(num_1.group())
print(num_2.group())
"""
'+447999999999'
'07999999999'
"""
As expressões regulares permitem que você especifique um padrão de texto para pesquisar. A maioria das pessoas sabe que podemos pesquisar coisas usando CTRL + F (Windows), mas se você não sabe exatamente o que está procurando, como poderia encontrá-lo? A resposta é procurar padrões.
Por exemplo, os números do Reino Unido seguem um padrão parecido: eles têm um zero no começo mais dez números ou +44 em vez de zero e dez números – o segundo caso mostra que está no formato internacional.
As expressões regulares economizam muito tempo. Se a gente fosse codificar regras para capturar as instâncias em nossa imagem em vez de regex, isso poderia levar mais de 10 linhas de código.
Aprender como as expressões regulares funcionam é essencial, mesmo que você não escreva código. A maioria dos editores de texto e processadores de texto modernos permitem que você use expressões regulares para localizar e substituir recursos.
#21 Tubulação Regex
import re
heros = re.compile(r"Super(man|woman|human)")
h1 = heros.search("This will find Superman")
h2 = heros.search("This will find Superwoman")
h3 = heros.search("This will find Superhuman")
print(h1.group())
print(h2.group())
print(h3.group())
"""
Superman
Superwoman
Superhuman
"""
As expressões regulares têm um caractere especial chamado pipe (|) que permite combinar uma de várias expressões, e podem ser usadas em qualquer lugar. Isso é super útil quando você tem vários padrões parecidos.
Por exemplo, “Super-Homem”, “Supermulher” e “Super-humano” têm todos o mesmo prefixo. Assim, você pode usar o pipe para manter a parte do padrão que se repete e mudar as partes que precisam ser diferentes. Mais uma vez, economizando seu tempo precioso.
Cuidado com a armadilha: se todas as expressões que você quer encontrar aparecerem no mesmo texto, a primeira ocorrência do texto a ser encontrado será retornada - ou seja, “Um texto de exemplo contendo Supermulher, Super-Homem, Super-Humano” retornaria Supermulher.
#22 O parâmetro “sep” da função de impressão
day = "04"
month = "10"
year = "2022"
print(day, month, year)
print(day, month, year, sep = "")
print(day, month, year, sep = ".")
"""
04 10 2022
04/10/2022
04.10.2022
"""
É assustador o número de programadores Python que não conhecem todas as capacidades da função print(). Se “Hello World” foi o seu primeiro programa, a função print() provavelmente foi uma das primeiras funções integradas que você aprendeu ao estudar Python. Usamos print() para mostrar mensagens formatadas na tela, mas a função print() tem muito mais a oferecer.
No código acima, mostramos diferentes maneiras de exibir nossa mensagem formatada. O parâmetro sep é um argumento opcional na função print() que nos permite especificar como os objetos devem ser separados se incluirmos mais de um.
O padrão é separá-los com um espaço, mas a gente mudou essa funcionalidade com nossas instruções de impressão - uma em que sep é definido como “” e outra em que sep é definido como “.”.
#23 Funções Lambda
def square(num:int) -> int:
return num ** 2
print(f"Function call: {square(4)}")
"""
Function call: 16
"""
square_lambda = lambda x: x**2
print(f"Lambda function: {square_lambda(4)}")
"""
Lambda functional: 16
"""
As funções Lambda levam você a um nível mais intermediário-avançado do que você pode fazer com Python – aprenda Python intermediário com este curso. À primeira vista, parecem complicados, mas são bem simples.
No nosso código de exemplo, só usamos um argumento, mas poderíamos ter usado vários se quiséssemos:
square = lambda a, b: a ** b
print(f"Lambda function: {square(4, 2)}")
"""
16
"""
Basicamente, a palavra-chave lambda deixa a gente criar funções pequenas, restritas e anônimas em uma linha só. Elas funcionam como uma função normal declarada com a palavra-chave def, só que essas funções não têm nome.
#24 O método “swapcase”
string = "SoMe RaNDoM sTriNg"
print(string.swapcase())
"""
sOmE rAndOm StRInG
"""
O método swapcase() é usado num objeto string pra gente poder mudar as letras maiúsculas pra minúsculas e vice-versa numa única linha de código. Não tem muitos casos em que usar o método swapcase() faz sentido, mas é legal saber que ele existe.
#25 O método “isalnum”
password = "ABCabc123"
print(password.isalnum())
"""
True
"""
Digamos que estamos criando um programa que exige que os usuários digitem uma senha, mas ela precisa ter uma combinação de números e letras. Podemos fazer isso com uma linha de código chamando a função isalnum() na instância da string.
O método verifica se todos os caracteres fazem parte do alfabeto (A-Za-z) e são numéricos (0-9). Um espaço ou símbolo (!#%$&? etc.) retornará False.
#26 Tratamento de exceções
def get_ration(x:int, y:int) -> int:
try:
ratio = x/y
except ZeroDivisionError:
y = y + 1
ratio = x/y
return ratio
print(get_ration(x=400, y=0))
"""
400.0
"""
Os programas Python param quando encontram um erro.
Às vezes, a gente não quer esse comportamento, tipo quando um usuário final está interagindo com o nosso código. Seria muito ruim se nosso código fosse encerrado antes da hora nesse caso?
Tem várias maneiras de pensar sobre como lidar com o caso excepcional. A maioria dos programadores Python geralmente concorda que é mais fácil pedir perdão do que pedir permissão. Isso quer dizer que eles preferem pegar um erro levantado, dando um contexto que consiga lidar com uma exceção. A ideia por trás disso é que não adianta perder tempo tentando se proteger contra todos os casos excepcionais.
Mas isso só vale quando existe um jeito de lidar com um problema depois que ele rola.
#27 Identificando as diferenças nas listas
list_1 = [1, 3, 5, 7, 8]
list_2 = [1, 2, 3, 4, 5, 6, 7, 8, 9]
solution_1 = list(set(list_2) - set(list_1))
solution_2 = list(set(list_1) ^ set(list_2))
solution_3 = list(set(list_1).symmetric_difference(set(list_2)))
print(f"Solution 1: {solution_1}")
print(f"Solution 2: {solution_2}")
print(f"Solution 3: {solution_3}")
"""
Solution 1: [9, 2, 4, 6]
Solution 2: [2, 4, 6, 9]
Solution 3: [2, 4, 6, 9]
"""
Aqui estão três jeitos diferentes de comparar a diferença entre duas listas em Python.
Observação: A menos que você tenha certeza de que a lista_1 é um subconjunto da lista_2, a solução 1 não é igual às outras duas soluções.
#28 Args e Kwargs
def some_function(*args, **kwargs):
print(f"Args: {args}")
print(f"Kwargs: {kwargs}")
some_function(1, 2, 3, a=4, b=5, c=6)
"""
Args: (1, 2, 3)
Kwargs: {'a': 4, 'b': 5, 'c': 6}
"""
Usamos *args e **kwargs como parâmetros para uma função quando não sabemos quantas variáveis nossa função deve esperar.
O parâmetro *args permite passar um número variável de parâmetros para uma função quando ela não é declarada com palavra-chave (ou seja, os parâmetros que passamos não precisam de um nome associado). Por outro lado, o parâmetro **kwargs permite passar um número arbitrário de parâmetros com palavras-chave para uma função.
Na verdade, as palavras *args e **kwargs não são tão mágicas assim: a verdadeira mágica está nos asteriscos (*). Isso quer dizer que a gente poderia ter usado qualquer palavra depois dos asteriscos, mas o uso de args e kwargs é bem comum e é o padrão entre os desenvolvedores Python.
#29 A Elipse
print(...)
"""
Ellipsis
"""
def some_function():
...
# Alternative solution
def another_function():
pass
O Ellipsis é um objeto Python que pode ser chamado usando uma sequência de três pontos (...) ou chamando o próprio objeto (Ellipsis).
O uso mais legal é pra acessar e cortar matrizes multidimensionais no NumPy, tipo assim:
import numpy as np
arr = np.array([[2,3], [1,2], [9,8]])
print(arr[...,0])
"""
[2 1 9]
"""
print(arr[...])
"""
[[2 3]
[1 2]
[9 8]]
"""
Mas outro uso da elipse é como um espaço reservado em uma função não implementada.
Isso quer dizer que você poderia passar Ellipsis, ..., ou pass, e todos eles ainda seriam válidos.
#30 Compreensão de listas
even_numbers = [x for x in range(10) if x % 2 == 0 and x != 0]
print(even_numbers)
"""
[2, 4, 6, 8]
"""
Nosso último truque em Python é a compreensão de listas, uma maneira elegante de criar uma lista a partir de outra sequência. Eles permitem que você faça uma lógica e filtragem sofisticadas, como fizemos no código acima.
Tem outras maneiras de chegar ao mesmo objetivo; por exemplo, a gente poderia ter usado uma função lambda assim:
even_numbers = list(filter(lambda x: x % 2 ==0 and x != 0, range(10)))
print(even_numbers)
"""
[0, 2, 4, 6, 8]
"""
Mas vários Pythonistas diriam que essa solução é bem menos legível do que a compreensão de lista.
Dá uma olhada nesse tutorial pra saber mais sobre compreensões de listas em Python.