Moduły (i pakiety) to świetny sposób w Pythonie na podzielenie programu na oddzielne przestrzenie nazw, co wydaje się być niejawnym celem tego pytania. Rzeczywiście, gdy uczyłem się podstaw Pythona, byłem sfrustrowany brakiem funkcji zakresu blokowego. Jednak gdy zrozumiałem moduły Pythona, mogłem bardziej elegancko zrealizować swoje poprzednie cele bez potrzeby stosowania zakresu blokowego.
Jako motywację i aby skierować ludzi we właściwym kierunku, myślę, że warto podać wyraźne przykłady niektórych konstrukcji określania zakresu w Pythonie. Najpierw wyjaśnię moją nieudaną próbę użycia klas Pythona do zaimplementowania zakresu blokowego. Następnie wyjaśniam, w jaki sposób osiągnąłem coś bardziej użytecznego przy użyciu modułów Pythona. Na koniec przedstawiam praktyczne zastosowanie pakietów do ładowania i filtrowania danych.
Próba zakresu blokowego z klasami
Przez kilka chwil myślałem, że osiągnąłem zakres blokowy, wklejając kod wewnątrz deklaracji klasy:
x = 5
class BlockScopeAttempt:
x = 10
print(x)
print(x)
Niestety to się psuje, gdy funkcja jest zdefiniowana:
x = 5
class BlockScopeAttempt:
x = 10
print(x)
def printx2():
print(x)
printx2()
Dzieje się tak, ponieważ funkcje zdefiniowane w klasie używają zasięgu globalnego. Najłatwiejszym (choć nie jedynym) sposobem rozwiązania tego problemu jest jawne określenie klasy:
x = 5
class BlockScopeAttempt:
x = 10
print(x)
def printx2():
print(BlockScopeAttempt.x)
printx2()
Nie jest to takie eleganckie, ponieważ trzeba różnie pisać funkcje w zależności od tego, czy są zawarte w klasie.
Lepsze wyniki dzięki modułom Pythona
Moduły są bardzo podobne do klas statycznych, ale z mojego doświadczenia wynika, że moduły są znacznie czystsze. Aby zrobić to samo z modułami, tworzę plik wywołany my_module.py
w bieżącym katalogu roboczym z następującą zawartością:
x = 10
print(x)
def printx():
global x
print(x)
Następnie w moim głównym pliku lub sesji interaktywnej (np. Jupyter) robię
x = 5
import my_module
my_module.printx()
print(x)
Jako wyjaśnienie, każdy plik Pythona definiuje moduł, który ma własną globalną przestrzeń nazw. Importowanie modułu umożliwia dostęp do zmiennych w tej przestrzeni nazw za pomocą .
składni.
Jeśli pracujesz z modułami w sesji interaktywnej, możesz wykonać te dwie linie na początku
%load_ext autoreload
%autoreload 2
a moduły zostaną automatycznie załadowane ponownie, gdy odpowiednie pliki zostaną zmodyfikowane.
Pakiety do ładowania i filtrowania danych
Idea pakietów jest niewielkim rozszerzeniem koncepcji modułów. Pakiet to katalog zawierający (prawdopodobnie pusty) __init__.py
plik, który jest wykonywany podczas importu. Dostęp do modułów / pakietów w tym katalogu można uzyskać za pomocą .
składni.
Do analizy danych często potrzebuję odczytać duży plik danych, a następnie interaktywnie zastosować różne filtry. Odczytanie pliku zajmuje kilka minut, więc chcę to zrobić tylko raz. Na podstawie tego, czego nauczyłem się w szkole o programowaniu obiektowym, uważałem, że kod do filtrowania i ładowania należy pisać jako metody w klasie. Główną wadą tego podejścia jest to, że jeśli następnie ponownie zdefiniuję moje filtry, definicja mojej klasy ulegnie zmianie, więc muszę ponownie załadować całą klasę, w tym dane.
Obecnie w Pythonie definiuję pakiet o nazwie, my_data
który zawiera podmoduły o nazwach load
i filter
. Wewnątrz filter.py
mogę zrobić względny import:
from .load import raw_data
Jeśli zmodyfikuję filter.py
, to autoreload
wykryję zmiany. Nie ładuje się ponownie load.py
, więc nie muszę ponownie ładować moich danych. W ten sposób mogę prototypować mój kod filtrujący w notatniku Jupyter, zawijać go jako funkcję, a następnie wycinać i wklejać z mojego notatnika bezpośrednio do filter.py
. Zrozumienie tego zrewolucjonizowało mój przepływ pracy i przekształciło mnie ze sceptyka w wierzącego w „Zen Pythona”.
One purpose (of many) is to improve code readability
- Kod Pythona, napisany poprawnie (tj. Zgodny z zen w Pythonie ) nie potrzebuje takiego ozdobnika, aby był czytelny. W rzeczywistości jest to jedna z (wielu) rzeczy, które lubię w Pythonie.