Módulos (e pacotes) são uma ótima maneira Pythônica de dividir seu programa em namespaces separados, o que parece ser um objetivo implícito desta questão. Na verdade, enquanto estava aprendendo o básico do Python, me senti frustrado com a falta de um recurso de escopo de bloco. No entanto, uma vez que entendi os módulos Python, pude realizar com mais elegância meus objetivos anteriores sem a necessidade de escopo de bloco.
Como motivação, e para apontar as pessoas para a direção certa, acho que é útil fornecer exemplos explícitos de algumas das construções de escopo do Python. Primeiro, explico minha tentativa fracassada de usar classes Python para implementar o escopo do bloco. A seguir, explico como consegui algo mais útil usando módulos Python. No final, descrevo uma aplicação prática de pacotes para carregar e filtrar dados.
Tentando escopo de bloco com classes
Por alguns momentos, pensei que havia alcançado o escopo do bloco colocando o código dentro de uma declaração de classe:
x = 5
class BlockScopeAttempt:
x = 10
print(x) # Output: 10
print(x) # Output: 5
Infelizmente, isso falha quando uma função é definida:
x = 5
class BlockScopeAttempt:
x = 10
print(x) # Output: 10
def printx2():
print(x)
printx2() # Output: 5!!!
Isso ocorre porque as funções definidas em uma classe usam escopo global. A maneira mais fácil (embora não a única) de corrigir isso é especificar explicitamente a classe:
x = 5
class BlockScopeAttempt:
x = 10
print(x) # Output: 10
def printx2():
print(BlockScopeAttempt.x) # Added class name
printx2() # Output: 10
Isso não é tão elegante porque é preciso escrever funções de maneira diferente, dependendo se estão ou não contidas em uma classe.
Melhores resultados com módulos Python
Módulos são muito semelhantes às classes estáticas, mas os módulos são muito mais limpos na minha experiência. Para fazer o mesmo com os módulos, crio um arquivo chamado my_module.py
no diretório de trabalho atual com o seguinte conteúdo:
x = 10
print(x) # (A)
def printx():
global x
print(x) # (B)
Então, em meu arquivo principal ou sessão interativa (por exemplo, Jupyter), eu faço
x = 5
import my_module # Output: 10 from (A)
my_module.printx() # Output: 10 from (B)
print(x) # Output: 5
Como explicação, cada arquivo Python define um módulo que possui seu próprio namespace global. Importar um módulo permite que você acesse as variáveis neste namespace com o.
sintaxe.
Se você estiver trabalhando com módulos em uma sessão interativa, você pode executar essas duas linhas no início
%load_ext autoreload
%autoreload 2
e os módulos serão recarregados automaticamente quando seus arquivos correspondentes forem modificados.
Pacotes para carregar e filtrar dados
A ideia de pacotes é uma pequena extensão do conceito de módulos. Um pacote é um diretório que contém um __init__.py
arquivo (possivelmente em branco) , que é executado na importação. Módulos / pacotes dentro deste diretório podem ser acessados com a .
sintaxe.
Para análise de dados, geralmente preciso ler um grande arquivo de dados e, em seguida, aplicar interativamente vários filtros. Ler um arquivo leva vários minutos, então quero fazer isso apenas uma vez. Com base no que aprendi na escola sobre programação orientada a objetos, costumava acreditar que se deveria escrever o código para filtrar e carregar como métodos em uma classe. Uma grande desvantagem dessa abordagem é que, se eu redefinir meus filtros, a definição de minha classe muda, então tenho que recarregar a classe inteira, incluindo os dados.
Hoje em dia com Python, defino um pacote chamado my_data
que contém submódulos chamados load
e filter
. Dentro de filter.py
eu posso fazer uma importação relativa:
from .load import raw_data
Se eu modificar filter.py
, autoreload
detectarei as mudanças. Ele não recarrega load.py
, então não preciso recarregar meus dados. Dessa forma, posso criar um protótipo de meu código de filtragem em um bloco de notas Jupyter, envolvê-lo como uma função e, em seguida, cortar e colar de meu bloco de notas diretamente no filter.py
. Descobrir isso revolucionou meu fluxo de trabalho e me transformou de um cético em um crente no "Zen de Python".
One purpose (of many) is to improve code readability
- O código Python, escrito corretamente (ou seja, seguindo o zen do python ) não precisa desse enfeite para ser legível. Na verdade, é uma das (muitas) coisas de que gosto no Python.