Kiedy kodujesz w innych językach, czasami tworzysz zakres blokowy, na przykład:
statement
...
statement
{
statement
...
statement
}
statement
...
statement
Jednym z celów (z wielu) jest poprawa czytelności kodu: pokazanie, że pewne instrukcje tworzą jednostkę logiczną lub że pewne zmienne lokalne są używane tylko w tym bloku.
Czy istnieje idiomatyczny sposób zrobienia tego samego w Pythonie?
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.__exit__
iwith
oświadczać, zmieniając,globals()
ale nie udało mi się.Odpowiedzi:
Nie, nie ma obsługi języków do tworzenia zakresu blokowego.
Następujące konstrukcje tworzą zakres:
źródło
Idiomatycznym sposobem w Pythonie jest zachowanie krótkich funkcji. Jeśli myślisz, że tego potrzebujesz, zrefaktoryzuj swój kod! :)
Python tworzy nowy zakres dla każdego modułu, klasy, funkcji, wyrażenia generatora, rozumienia dyktowania i zestawu, a w Pythonie 3.x także dla każdego rozumienia listy. Poza tym w funkcjach nie ma zagnieżdżonych zakresów.
źródło
Możesz zrobić coś podobnego do zakresu blokowego C ++ w Pythonie, deklarując funkcję wewnątrz swojej funkcji, a następnie natychmiast ją wywołując. Na przykład:
def my_func(): shared_variable = calculate_thing() def do_first_thing(): ... = shared_variable do_first_thing() def do_second_thing(): foo(shared_variable) ... do_second_thing()
Jeśli nie masz pewności, dlaczego chcesz to zrobić, to ten film może Cię przekonać.
Podstawową zasadą jest możliwie najściślejsze określanie zakresu wszystkiego bez wprowadzania jakichkolwiek „śmieci” (dodatkowych typów / funkcji) w szerszym zakresie niż jest to absolutnie wymagane - nic innego nie chce używać
do_first_thing()
metody, na przykład, więc nie należy jej ograniczać poza zakres funkcja wywoływania.źródło
Zgadzam się, że nie ma zakresu blokowego. Ale jedno miejsce w Pythonie 3 sprawia, że wygląda na to, że ma zasięg blokowy.
co się stało, co dało ten wygląd? To działało poprawnie w Pythonie 2, ale aby zatrzymać wyciek zmiennych w Pythonie 3, wykonali tę sztuczkę i ta zmiana sprawia, że wygląda to tak, jakby miało tutaj zasięg blokowy.
Pozwól mi wyjaśnić.
Zgodnie z ideą zakresu, gdy wprowadzamy zmienne o takich samych nazwach w tym samym zakresie, należy zmodyfikować jego wartość.
to właśnie dzieje się w Pythonie 2
>>> x = 'OLD' >>> sample = [x for x in 'NEW'] >>> x 'W'
Ale w Pythonie 3, mimo że zmienna o tej samej nazwie jest wprowadzona, nie zastępuje, rozumienie listy z jakiegoś powodu działa jak piaskownica i wydaje się, że tworzy w niej nowy zakres.
>>> x = 'OLD' >>> sample = [x for x in 'NEW'] >>> x 'OLD'
a ta odpowiedź jest sprzeczna ze stwierdzeniem answerer @ Thomas . Jedynym sposobem na utworzenie zakresu są funkcje, klasy lub moduły, ponieważ wygląda to jak jedno inne miejsce tworzenia nowego zakresu.
źródło
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) # Output: 10 print(x) # Output: 5
Niestety to się psuje, gdy funkcja jest zdefiniowana:
x = 5 class BlockScopeAttempt: x = 10 print(x) # Output: 10 def printx2(): print(x) printx2() # Output: 5!!!
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) # Output: 10 def printx2(): print(BlockScopeAttempt.x) # Added class name printx2() # Output: 10
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) # (A) def printx(): global x print(x) # (B)
Następnie w moim głównym pliku lub sesji interaktywnej (np. Jupyter) robię
x = 5 import my_module # Output: 10 from (A) my_module.printx() # Output: 10 from (B) print(x) # Output: 5
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 nazwachload
ifilter
. Wewnątrzfilter.py
mogę zrobić względny import:from .load import raw_data
Jeśli zmodyfikuję
filter.py
, toautoreload
wykryję zmiany. Nie ładuje się ponownieload.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 dofilter.py
. Zrozumienie tego zrewolucjonizowało mój przepływ pracy i przekształciło mnie ze sceptyka w wierzącego w „Zen Pythona”.źródło