Konkretne przykłady maksymy „tylko jeden sposób na to” Pythona [zamknięte]

34

Uczę się języka Python i intryguje mnie następujący punkt w PEP 20 Zen of Python :

Powinien być jeden - a najlepiej tylko jeden - oczywisty sposób na zrobienie tego. Chociaż na początku taki sposób może nie być oczywisty, chyba że jesteś Holendrem.

Czy ktoś mógłby podać jakieś konkretne przykłady tej maksymy? Szczególnie interesuje mnie kontrast z innymi językami, takimi jak Ruby. Częścią filozofii projektowania Rubiego (myślę, że pochodzi z Perla?) Jest to, że wiele sposobów na zrobienie tego jest Dobrą Rzeczą. Czy ktoś może podać kilka przykładów pokazujących zalety i wady każdego podejścia. Uwaga: nie szukam odpowiedzi, która jest lepsza (która jest prawdopodobnie zbyt subiektywna, aby na nią kiedykolwiek odpowiedzieć), ale raczej obiektywne porównanie dwóch stylów.

Charles Roper
źródło

Odpowiedzi:

47

W porównaniu do języków takich jak Perl, Python ma ograniczoną liczbę konstrukcji kontrolnych:

  • tylko ifi nie unless,
  • tylko forże iteracje nad sekwencjami i brak foreachlub C-stylu for,
  • tylko whileto sprawdza warunek w każdej pętli i nie do-while,
  • tylko if-elifi nie switch,
  • istnieje tylko jedna konstrukcja komentarza #, a dla każdej linii można stwierdzić, czy jest komentowana, czy nie, bez patrzenia na poprzednie linie.

Ponadto istnieje prawie jeden sposób na wcięcie źródła; większość przypadków twórczego wcięcia jest syntaktycznie wykluczona.

To sprawia, że ​​parsowanie źródła Python jest łatwiejsze dla ludzi.

Próbuje się być minimalnym, ale kompletnym we wbudowanych typach i standardowej bibliotece.

  • dla listy zmiennych można użyć jedynego wbudowanego listtypu; jest to O (1) dla większości operacji i nigdy nie musisz wybierać właściwej implementacji,
  • w przypadku niezmiennych listów po prostu używasz tego tupletypu,
  • w przypadku map korzystasz z jedynego wbudowanego, dictktóry jest cholernie wydajny w większości przypadków, nie musisz zastanawiać się, którą implementację zastosować.

Python 3 rozszerza to na liczby całkowite: bez względu na to, jak duża jest twoja liczba całkowita, używasz tego samego typu i nigdy nie dbasz o przymus.

Python próbuje uniknąć cukru syntaktycznego. Ale czasami dodaje cukier syntaktyczny, aby oczywisty sposób był oczywisty. Możesz pisać if foo is not Nonezamiast tego, if not (foo is None)ponieważ „nie jest” jest w specjalnej obudowie. Nadal foo is not Noneczyta łatwo, nie można go źle interpretować i nie musisz myśleć, po prostu piszesz oczywiste rzeczy.

Oczywiście większość bardziej złożonych rzeczy w Pythonie można wykonać na kilka sposobów. Możesz dodawać metody do klas poprzez deklarację lub proste przypisanie szczelin, możesz przekazywać argumenty do funkcji na wiele kreatywnych sposobów itp. To tylko dlatego, że wewnętrzne elementy języka są w większości odsłonięte.

Kluczem jest to, że zawsze istnieje jeden sposób, który ma być najlepszy, obejmujący wszystko. Jeśli istnieją inne sposoby, nie zostały one dodane jako równe alternatywy (jak ifi unless), a jedynie ujawniają wewnętrzne funkcjonowanie. Powoli, ale konsekwentnie, takie alternatywy są przestarzałe (nie wyeliminowane!) Poprzez ulepszenie znanego najlepszego mechanizmu.

Dekoratorzy zawijają wywołania funkcji AOP. Przed wersją 2.6 musiałeś użyć __metaclass__członka magii, aby zadeklarować metaklasę klasy; teraz możesz również użyć do tego tej samej składni dekoratora. Przed wersją 3.0 istniały dwa rodzaje ciągów, zorientowane na bajty i Unicode, które można przypadkowo zmiksować. Teraz masz jedyny Unicode stri jedyny binarny bytes, którego nie można pomyłkowo wymieszać.

9000
źródło
3
Dla przypomnienia, nie zapomnij o """komentarzach (dokumentach). Obejmują one wiele linii.
asthasr
8
Potrójne literały są tylko ciągami znaków, takimi samymi jak cudzysłowy, ale mogą obejmować wiele linii bez uciekania końców linii. Dosłowny ciąg znaków zaraz po deklaracji jest uważany za ciąg dokumentu i nie jest to komentarz, zwykle jest dostępny jako __doc__atrybut. Ale ciągi to obszar, w którym Python zdecydowanie oferuje wiele „właściwych sposobów”: użyj pojedynczego, podwójnego lub potrójnego cudzysłowu, domyślnie połącz sąsiednie literały, użyj rsurowych literałów itp.
9000
1
Myślę, że komentarz @ syrion dotyczył tego, że „zawsze możesz zdecydować, czy wiersz jest komentowany, czy nie, po prostu patrząc na niego”, co nie jest prawdą z powodu ciągów „” „.
blubb
2
„Dzięki temu parsowanie źródła w języku Python jest łatwiejsze dla ludzi”. <- to subiektywne
jiggy
Jak zmieniła się deklaracja metaklasy w wersji 2.7? Nie można znaleźć wzoru dekoratora w dokumentach 2.7 dla metaklas.
Nick T
10

Kolejnymi przykładami są:
len()jest funkcją zamiast metody występującej w każdej sekwencji; jeśli porównać z Java masz .length, .size(), .getSize()oraz inne metody, aby znaleźć liczbę elementów w sekwencji.

Innym przykładem jest fakt, że .join()jest to stringmetoda, a nie metoda obecna w każdej sekwencji. Nie musisz wiedzieć, czy parametr łączenia jest listą, zestawem, zrozumieniem czy czymkolwiek innym, zadziała.

Vito De Tullio
źródło
8

W C istnieje wiele możliwych sposobów zwiększenia wartości zmiennej o jeden:

i++     // Post-increment, returns the number before the increment
++i     // Pre-increment, returns the number after the increment
i += 1 

Każda kończy się zwiększenie wartości io 1, ale każdy jest nieco inny.

W Pythonie istnieje tylko jeden sposób; po prostu dodaj jeden.

i += 1

I chociaż istnieje więcej niż jeden prawidłowy sposób na zrobienie tego (np. i = i + 1), Robisz to samo z tymi samymi efektami ubocznymi.


źródło
1
Nie jestem ekspertem, ale ten przykład wydaje się dokładnie naruszać ideę „jedynego sposobu, aby to zrobić”. Mamy na to dwa sposoby, ale co jest bardziej oczywiste? Moim zdaniem pierwszy przykład jest bardziej oczywisty, podczas gdy drugi jest nieco bardziej zwięzły, ale nie mniej czytelny lub oczywisty dla każdego programisty, który wyszedł poza podstawy. Dziękuję za odpowiedź - to dobre jedzenie do namysłu.
Charles Roper
@Peter (i @Charles): W rzeczywistości i = i + 1jest przypisaniem, a nie przyrostem. W pythonie przyrostem jest i += 1. W językach C stylu można napisać i++, ++ii i += 1.
Josh K
2
Nie jestem pewien co do komentarza „dużo zamieszania”, wszystkie trzy przykłady C (brakujące i += 1, BTW) dają dokładnie taki sam wynik. Jedyny raz widzę, jak ludzie się mylą, kiedy zwiększają lub zmniejszają zmienną jako część większego wyrażenia, a to zwykle szybko naprawia się, czytając odpowiednią sekcję odnośnika językowego. Osobiście wybrałbym fakt, że możesz odwoływać się do piątego znaku struny przez jedno str[4]lub drugie *(str+4), ale może to było zbyt łatwe ...
TMN
2
@TMN: niektóre przypadki, takie jak takie, max(i++, ++i)których nie można szybko naprawić. C ma wiele „nieokreślonych” i „zależnych od implementacji” przypadków zachowania, wszystko z dobrych powodów - ale każdy może stworzyć pułapkę.
9000
@TMN: Nie wspominając o 4 [str] (poprawne w C, może nie być poprawne w C ++).
Vatine
6

Inną możliwością mogą być listy ze zrozumieniem. W Pythonie możesz to zrobić:

new_list = []
    for item in list_of_items:
       if item < 10:
           new_list.append(item)

Ale „oczywistym” sposobem (jeśli jesteś Holendrem lub jesteś bardziej zaznajomiony z Pythonem) zrobienia tego byłoby zrozumienie listy:

new_list = [item for item in list_of_items if item < 10]

Jest krótszy, nowa_lista jest tworzona w jednym kroku, wierzę, że działa szybciej, i jest elegancki. Z drugiej strony, można argumentować, że wydaje się mniej wyraźny, ale myślę, że kiedy się do tego przyzwyczaisz, jest to równie wyraźne.

Chelonski
źródło
W przypadku wcięcia i kodu: dodaj 4 spacje, a następnie uwzględni wcięcie.
Inca