2012-07-07 11 views
9

Nie zamierzam po prostu marnować czasu, ale: przyszło ci również do głowy, podczas używania Pythona with stwierdzenie, że naprawdę jest to sprzeczne z piątą linią "The Zen of Python "to idzie" Mieszkanie jest lepsze niż zagnieżdżone "? Czy każdy oświecony guru Python może podzielić się z nami swoimi spostrzeżeniami?zen Pythona vs z oświadczeniem - filozoficzne rozważanie

(zawsze okaże się, że jeden poziom wcięcia wyskakuje w moim kodu za każdym razem używam with zamiast f.close() ... i to nie jest tak, jak nie będę użycie try: ... finally: ... jakikolwiek a zatem korzyści z with nieruchomych wymykają się mnie, nawet jak dorosnę lubić i rozumieć Python więcej i więcej ...)


@glglgl (przepraszam, nie mogę znaleźć sposób, aby napisać kod w komentarzach): tak, ale jeśli Po przejściu do trybu with Twój kod stanie się:

try: 
    with file(...) as f: 
     ... 
except IOError: 
    ... 

... a używanie tylko bez, try jest tym, co ludzie robią w hackowskim kodzie "jednorazowego użytku", gdzie używają f.close() zamiast tak czy inaczej (co jest złe, ponieważ plik może nie zostać zamknięty, jeśli wyjątek jest rzucany przed ich f.close()), więc dla "hacky" kodu ludzie po prostu nie używają with ponieważ, nie wiem, myślę, że po prostu uważają, że jest to zbyt "fantazyjne" i dla dobrze zorganizowanego kodu, który nie przynosi Jakiekolwiek korzyści i tak, wydaje mi się, że nie ma dla mnie przypadku użycia rzeczywistego świata ... tak naprawdę zastanawiałem się nad tym.

+1

W większości przypadków po prostu pozwoliłem IOError propagować i przechwytywać je gdzie indziej. – Dikei

+0

Mimo że nie zgadzam się z przesłanką tego pytania, muszę dać ci rekwizyty, aby skłonić mnie do powrotu i ponownego przeczytania PEP [342] (http: //www.python.org/dev/peps/pep-0342 /) i [343] (http://www.python.org/dev/peps/pep-0343/) – kojiro

Odpowiedz

5

Tak, The Zen of Python stwierdza "Mieszkanie jest lepsze niż zagnieżdżone", jednak nie jest to jedyna cecha, na której nam zależy; stwierdza również, że "Prostota jest lepsza niż złożona". Piękno with polega na tym, że faktycznie przylega ona do zarówno tych zasad, co wyjaśnię poniżej.

Za każdym razem, gdy znajdujesz się w filozoficznym rozważaniu na temat funkcji w Pythonie, prawdopodobnie warto przejrzeć Python Enhancement Proposals (PEPs), aby przeczytać o motywacji tej funkcji. W tym przypadku PEP 343 -- The "with" Statement mówi się front w STRESZCZENIE:

Ten PEP dodaje nowe oświadczenie „z” do języka Python aby możliwy czynnik poza standardowe sposoby korzystania z try/finally wypowiedzi.

Faktoring out try/finally wyciągi czyni kod prostszym i bardziej czytelnym.

PEP 343 idzie jednak głębiej niż dostarczanie uproszczonego cukru syntaktycznego. Ustanawia ona protokół menedżera kontekstu:

Wyrażenie natychmiast po z kluczowych w rachunku jest „wyrażenie kontekstu”, jak to wyrażenie stanowi główną wskazówkę jak do środowiska wykonawczego kierownik kontekst ustanawia dla czas trwania treści wyciągu.

Za pomocą protokołu menedżera kontekstu, programy piszące API mogą pomóc ukryć złożoność i zapewnić poprawne pozyskanie/uwolnienie zasobów w kontekście wielowątkowym.

Ale prawdziwe piękno rachunku with jest pokazany w przykładzie 12 PEP 343 która wyjaśnia, że:

A „zagnieżdżone” kierownik kontekst, który automatycznie gniazduje dostarczonych konteksty od lewej do prawej, aby uniknąć nadmierne wcięcie.

Korzystanie kierownik nested() kontekst można wziąć kod, który wygląda tak:

with a as x: 
    with b as y: 
     with c as z: 
      # Perform operation 

i przekształcić go w ten sposób:

with nested(a, b, c) as (x, y, z): 
      # Perform operation 

Zauważ, że nested() został wprowadzony w Pythonie 2.5, ale od wersji 2.7 jest on przestarzały na korzyść tej wielokrotnej formy syntaktycznej menedżera kontekstu:

with a as x, b as y, c as z: 
      # Perform operation 

Oczywiście nie tylko jest to prostsze i bardziej czytelne, ale jest znacznie bardziej płaskie niż zagnieżdżone. Zatem stosując with jest po ścieżce 無爲 :)

UPDATE: W odpowiedzi na comments on Simeon Visser's answer Oto przykład, kiedy można używać wielu menedżerów kontekstowych, aby otworzyć więcej niż jeden plik na raz, gdy chcesz zip zawartość dwóch (lub więcej) plików ze sobą tak, że jeśli otwierając jeden z plików nie sprawi, że cała sprawa nie uda i prawidłowo zamknąć każdy plik, który został otworzony

from itertools import izip 
with open("/etc/passwd") as a, open("/etc/group") as b, open("/etc/shadow") as c: 
    for lines in izip(a,b,c): 
     print map(lambda x: x.split(':')[0], lines) 

uruchomić ten przykład dwa razy; raz jako root i raz jako zwykły użytkownik. Zakładając, że zapiszesz ten plik jako ziptogether.py najpierw spróbujesz wywołać go jako root z sudo python ziptogether.py i zakończy się pomyślnie, ale wywołanie go jako zwykłego użytkownika z python ziptogether.py zakończy się niepowodzeniem, ponieważ nie masz uprawnień do odczytu /etc/shadow. W przypadku niepowodzenia menedżer kontekstu upewni się, że pliki, które zostały pomyślnie otwarte przed niepowodzeniem, są poprawnie zamykane, gdy wykonywanie wykracza poza zakres oświadczenia with.

+1

kudos. piękna i pouczająca odpowiedź! – NeuronQ

+1

@NeuronQ Zaktualizowałem swoją odpowiedź, aby zauważyć, że 'nested()' jest przestarzałe i istnieje jeszcze prostsza forma składniowa dla wielu menedżerów kontekstów. – aculich

7

wspomniałeś już: To jest czystsze zrobić

f = file(...) 
try: 
    # do work on file 
finally: 
    f.close() 

niż tylko zamykanie po operacji plik - które nie zostałyby osiągnięte, jeśli wystąpi wyjątek.

Jeśli porównać try/finally do with, masz taki sam poziom wcięcia, więc nic nie tracisz. Jeśli jednak zajmujesz się wyjątkami, masz jeszcze jeden poziom wcięcia, który jest rzeczywiście przeciwko wspomnianemu punktowi Zen.

OTOH, with hermetyzuje rzeczy i sprawia, że ​​używanie ich jest łatwiejsze i czytelniejsze, a są to inne aspekty zen.

Wydaje mi się niemożliwe, aby zawsze dokładnie śledzić każdy aspekt Zen; czasami trzeba ważyć jeden z drugim. W tym przypadku "tracisz" jeden poziom wcięcia, ale uzyskujesz lepszą czytelność i łatwość konserwacji. Ta ostatnia wydaje się być dla mnie korzystna.

+0

Próbowałem ci odpowiedzieć, ale nie mogłem znaleźć sposobu na napisanie kod w komentarzach, więc dodałem moją odpowiedź do końca mojego pytania – NeuronQ

+0

@NeuronQ Zaktualizowałem odpowiedź – glglgl

+0

Zgadzam się: Elementy Zen Pythona często muszą być wymieniane przeciwko sobie. W tym przypadku argumentowałbym, że jeden poziom zagnieżdżania, który należy zainwestować, pozwala śledzić ** Jawny jest lepszy niż niejawny **: Gdy zobaczysz słowo "z", jest jasne, że zajdzie obsługa końca (zamiast w zależności od losu wykonania logiki). "Jawny jest lepszy od niejawnego" to numer 2 na liście Zen w Pythonie, więc * powinno często * trąbić mniej zagnieżdżając. Wszystko dobrze. –

7

Zauważ, że Zen Pythona mówi:

Proste jest lepsze niż skomplikowane.

Kompleks jest lepszy niż skomplikowany.

i

liczy się czytelność.

Używanie menedżera kontekstowe w rachunku with zapewnia wiele rzeczy:

  • poprawnego zachowania jako plik jest zawsze zamknięty
  • czytelność (with open(..) as f jest całkiem zrozumiałe)

Można wskaż jeden element w zen Python i argumentuj, że cały kod Pythona musi zawsze spełniać wszystkie elementy. Na przykład, jeśli minimalny poziom wcięcia w celu rozwiązania konkretnego problemu w czytelny i poprawny sposób wynosi cztery, to niech tak będzie: jeśli poziom wcięć wynoszący trzy sprawi, że kod będzie mniej czytelny, po prostu zostaw kod sam (cztery są dobre).

+0

To, co mówisz, jest bardzo, bardzo ogólne ... Masz rację, mówiąc o "egzekwowaniu prawidłowego zachowania", ponieważ byłoby to dobre dla szybkiego, hackowatego kodu, ale znalazłem, że dokładnie ludzie, którzy piszą "szybki, hacky kod", don użyj instrukcji with. Część czytelności jest subiektywna (znajduję jeszcze jeden poziom wcięcia, który jest mniej czytelny, i kiedy robisz "wiele plików z" jak "z otwartym (...) jako plik1, otwórz (...) jako plik3, otwórz (. ..) jako plik4: "kończy się bardzo nieczytelne, ponieważ musisz przewijać poziomo lub brzydko, ponieważ dzielisz instrukcję z wieloma wierszami – NeuronQ

+0

Czy istnieje dobry powód, aby otwierać cztery pliki w tym samym czasie? odczytać dane z plików wejściowych sekwencyjnie i tak samo dla zapisu danych do jednego lub więcej plików wyjściowych Chociaż mogę sobie wyobrazić sytuacje, w których potrzebne są cztery pliki –

+0

@NeuronQ: Nie widzę nic złego w łamaniu polecenia więcej linii, zwłaszcza gdy wyrównane są części 'open (...) as fX' Zgadzam się z Simeonem, że nie jest to typowy przypadek, w każdym razie, jest czytelny w taki sam sposób jak cztery następujące po sobie' fX = open (. ..) Użyto poleceń '' – pepr

1
"Flat is better than nested" 

No, co jest płaskim?

import thirdparty 
print "I Like Pie!" 

vs

import org.example.thirdparty.something 
System.out.println("I Like Cake") 

etc ...

Zen Pythona nie wystarczy wymusić limit wcięcia w kodzie. Zachęca cię do napisania czytelnego (a przez to lepszego) kodu. Jeśli twoja instrukcja with jest funkcją, do której dostęp mają tylko 3 warstwy obiektów (itp., one.two.three.func()), to jest problem.

W przeciwnym razie trzy poziomy wcięć są równie dobre, jak inne.

1

Powodem wolą with jest to, że nie trzeba powiązać ręcznie powiązanych operacji (jak open(...)/.close(); ale with konstrukcja jest bardziej ogólnie - nie tylko do pracy z plikami). Jest to ważne, zwłaszcza w przypadkach, gdy druga operacja może nie zostać wykonana z przyczyn, które nie są wyraźnie widoczne z kodu źródłowego. Mówisz maszynie , zajmij się tym dla mnie, a maszyna jest lepsza w przypadku niż człowiek. W ten sposób pozbędziesz się grupy nieprzyjemnych błędów, które mogą być trudne do znalezienia.

Nawiasem mówiąc, powinieneś użyć open(...) zamiast file(...). Python 3 nie wie nic o file(...) i będziesz musiał później naprawić swój kod.

+0

Rzeczywiście, może powinienem zbadać użycie 'with' z innymi rzeczami niż plikami, nigdy tak naprawdę nie dałem tej dobrej myśli. Tak, "open (...)" to jest to, czego używam, nie wiem, gdzie był mój umysł, kiedy napisałem 'file (...)' ale na szczęście nie wpłynęło to na znaczenie pytania. – NeuronQ

Powiązane problemy