Muitos artigos foram escritos sobre recursos interessantes do Python. Eles falam sobre como descompactar listas e tuplas em variáveis, sobre aplicação parcial de funções, sobre como trabalhar com objetos iteráveis. Mas no Python há muito mais. O autor do artigo que estamos traduzindo hoje diz que deseja falar sobre alguns dos recursos do Python que ele usa. Ao mesmo tempo, ele ainda não encontrou uma descrição dessas possibilidades, semelhante à aqui apresentada. É possível que você não tenha lido sobre eles em nenhum outro lugar.

Limpando dados da sequência de entrada
A tarefa de limpar os dados inseridos pelo usuário é relevante para praticamente qualquer programa. Geralmente, esse processamento de entrada se resume à conversão de caracteres para maiúsculas ou minúsculas. Às vezes, os dados podem ser limpos usando uma expressão regular. Mas nos casos em que a tarefa é complicada, você pode aplicar uma maneira mais bem-sucedida de resolvê-la. Por exemplo - isto:
user_input = "This\nstring has\tsome whitespaces...\r\n" character_map = { ord('\n') : ' ', ord('\t') : ' ', ord('\r') : None } user_input.translate(character_map)
Aqui você pode ver como os caracteres de espaço em branco
"\n"
e
"\t"
são substituídos por espaços regulares e como o caractere
"\r"
é completamente removido da string. Este é um exemplo simples, mas podemos estendê-lo criando tabelas de remapeamento de caracteres grandes usando o pacote
unicodedata
e sua função
combining()
. Essa abordagem permite remover das linhas tudo o que não é necessário lá.
Obtendo fatias do iterador
Se você tentar obter uma fatia de um iterador, encontrará um erro
TypeError
, que indica que não é possível assinar o objeto gerador. No entanto, esse problema pode ser resolvido:
import itertools s = itertools.islice(range(50), 10, 20)
Usando o método
itertools.islice
,
itertools.islice
pode criar um objeto
islice
, que é um iterador que
islice
elementos necessários. No entanto, é importante observar aqui que essa construção usa todos os elementos do gerador até o início da fatia e todos os elementos no objeto
islice
.
Ignorar o início do objeto iterável
Às vezes, você precisa trabalhar com um arquivo que, como você sabe, começa com um certo número de linhas desnecessárias - como linhas com comentários. Para pular essas linhas, é possível recorrer novamente aos
itertools
do
itertools
:
string_from_file = """ // Author: ... // License: ... // // Date: ... Actual content... """ import itertools for line in itertools.dropwhile(lambda line: line.startswith("//"), string_from_file.split("\n")): print(line)
Esse código retorna apenas linhas após o bloco de comentários localizado no início do arquivo. Essa abordagem pode ser útil quando você precisa descartar apenas elementos (no nosso caso, linhas) no início do objeto iterável, mas seu número exato não é conhecido.
Funções que suportam apenas argumentos nomeados (kwargs)
Para possibilitar o uso de uma determinada função para que apenas argumentos nomeados possam ser transmitidos a ela, você pode fazer o seguinte:
def test(*, a, b): pass test("value for a", "value for b")
Isso pode ser útil para melhorar a compreensibilidade do código. Como você pode ver, nosso problema é facilmente resolvido usando o argumento
*
na frente da lista de argumentos nomeados. Aqui, o que é bastante óbvio, você também pode usar argumentos posicionais - se você os colocar antes do argumento
*
.
Criando objetos que suportam a instrução with
Todo mundo sabe como, por exemplo, abrir um arquivo ou, possivelmente, como definir um bloqueio usando a instrução
with
. Mas é possível implementar independentemente o mecanismo de controle de bloqueio? Sim, isso é bem real. O protocolo de gerenciamento do contexto de execução é implementado usando os métodos
__exit__
e
__exit__
:
class Connection: def __init__(self): ... def __enter__(self):
Essa é a maneira mais comum de implementar os recursos do gerenciador de contexto em Python, mas a mesma coisa pode ser feita mais facilmente:
from contextlib import contextmanager @contextmanager def tag(name): print(f"<{name}>") yield print(f"</{name}>") with tag("h1"): print("This is Title.")
Aqui, o protocolo de gerenciamento de contexto é implementado usando o decorador de gerenciador de
contextmanager
. A primeira parte da função de
tag
(antes do
yield
) é executada quando você entra no bloco
with
. Este bloco é então executado e, em seguida, o restante da função de
tag
é executado.
Economize memória com __slots__
Se você já gravou programas que criam um número realmente grande de instâncias de uma determinada classe, poderá perceber que esses programas podem inesperadamente exigir muita memória. Isso ocorre porque o Python usa dicionários para representar os atributos das instâncias de classe. Isso tem um bom efeito no desempenho, mas, em termos de consumo de memória, é ineficiente. Geralmente, no entanto, esse recurso não causa problemas. No entanto, se você estiver com falta de memória em uma situação semelhante, tente usar o atributo
__slots__
:
class Person: __slots__ = ["first_name", "last_name", "phone"] def __init__(self, first_name, last_name, phone): self.first_name = first_name self.last_name = last_name self.phone = phone
Aqui, quando declaramos o atributo
__slots__
, o Python usa uma pequena matriz de tamanho fixo para armazenar os atributos, não um dicionário. Isso reduz seriamente a quantidade de memória necessária para cada instância da classe. Existem algumas desvantagens em usar o atributo
__slots__
. Portanto, usando-o, não podemos declarar novos atributos, estamos limitados apenas àqueles que estão em
__slots__
. Além disso, as classes com o atributo
__slots__
não podem usar herança múltipla.
Limites de CPU e memória
Se, em vez de otimizar o programa ou melhorar a maneira como ele usa o processador, você só precisa definir uma restrição estrita nos recursos disponíveis, pode usar a biblioteca apropriada:
import signal import resource import os
Isso mostra a limitação do tempo do processador e tamanho da memória. Para limitar o uso do processador pelo programa, primeiro obtemos os valores de limites não rígidos (flexíveis) e rígidos (rígidos) para um recurso específico (
RLIMIT_CPU
). Em seguida, definimos o limite usando um certo número de segundos especificado pelo argumento
seconds
e o valor-limite obtido anteriormente. Depois disso, registramos o manipulador de
signal
que, quando o tempo do processador alocado para o programa é excedido, inicia o procedimento de saída. No caso da memória, obtemos novamente valores para limites não rígidos e rígidos, após os quais definimos o limite usando o método
setrlimit
, para o qual passamos o tamanho da restrição (
size
) e o valor obtido anteriormente do limite rígido.
Controlando o que pode ser importado do módulo e o que não pode
Algumas linguagens possuem mecanismos de exportação extremamente claros a partir de módulos de variáveis, métodos e interfaces. Por exemplo, apenas as entidades cujos nomes começam com uma letra maiúscula são exportadas para Golang. No Python, tudo é exportado. Mas apenas até o atributo
__all__
ser
__all__
:
def foo(): pass def bar(): pass __all__ = ["bar"]
No exemplo acima, apenas a função
bar
será exportada. E se você deixar o atributo
__all__
branco, nada será exportado do módulo. Tentar importar algo desse módulo gerará um erro
AttributeError
.
Simplifique a criação de operadores de comparação
Existem muitos operadores de comparação. Por exemplo,
__lt__
,
__le__
,
__gt__
,
__ge__
. Poucas pessoas vão gostar da perspectiva de sua implementação para uma determinada classe. Existe alguma maneira de simplificar essa tarefa chata? Sim, você pode - com a ajuda do decorador
functools.total_ordering
:
from functools import total_ordering @total_ordering class Number: def __init__(self, value): self.value = value def __lt__(self, other): return self.value < other.value def __eq__(self, other): return self.value == other.value print(Number(20) > Number(3)) print(Number(1) < Number(5)) print(Number(15) >= Number(15)) print(Number(10) <= Number(2))
O decorador
functools.total_ordering
usado aqui para simplificar o processo de implementação da ordenação de instâncias de classe. Para garantir sua operação, é necessário apenas que os operadores de comparação
__lt__
e
__eq__
. É o mínimo que um decorador precisa para construir os demais operadores de comparação.
Sumário
Isso não quer dizer que tudo o que falei aqui seja absolutamente necessário no trabalho diário de todo programador Python. Mas algumas das técnicas apresentadas aqui, de tempos em tempos, podem ser muito úteis. Além disso, eles são capazes de simplificar a solução de problemas, cuja solução usual pode exigir muito código e uma grande quantidade de trabalho monótono. Além disso, gostaria de observar que tudo o que foi discutido faz parte da biblioteca padrão do Python. Para ser sincero, alguns desses recursos parecem inesperados para a biblioteca padrão. Isso sugere que, se alguém vai implementar no Python algo que não parece muito comum, ele deve primeiro vasculhar a biblioteca padrão. Se você não encontrar o que precisa imediatamente, talvez valha a pena cavar novamente, com muito cuidado. É verdade que, se uma pesquisa completa não foi bem-sucedida, provavelmente o que você precisa não está lá. E, nesse caso, você deve recorrer a bibliotecas de terceiros. Neles, pode ser encontrado definitivamente.
Caros leitores! Você conhece algum recurso padrão do Python que possa parecer incomum à primeira vista chamado de "padrão"?
