Czym różni się podział wierszy w Pythonie od automatycznych średników JavaScript?


41

JavaScript ma funkcję o nazwie Automatyczne wstawianie średnika, w której w zasadzie jeśli parser napotka niepoprawny token, a ostatni token przed nim był łamaniem linii, wówczas parser wstawi średnik tam, gdzie jest podział linii. Umożliwia to w zasadzie pisanie całego kodu javascript bez średników, ale trzeba pamiętać o niektórych przypadkach krawędzi, głównie jeśli masz słowo kluczowe return, a następnie wartość, którą chcesz zwrócić w nowym wierszu.

function test(){
    // This will return 'undefined', because return is a valid statement
    // and  "john" is a valid statement on its own.
    return 
          "john"
}

Z powodu tych błędów istnieją dziesiątki artykułów o tytułach takich jak „Automatyczne wstawianie średnika jest złe”, „Zawsze używaj średników w JavaScript” itp.

Ale w Pythonie nikt nigdy nie używa średników i ma dokładnie takie same gotchas.

def test():
    # This will return 'undefined', because return is a valid statement
    # and  "john" is a valid statement on its own.
    return 
    "john"

Działa dokładnie tak samo, a jednak nikt nie boi się śmiertelnie zachowania Pythona.

Myślę, że przypadki, w których javascript zachowuje się źle, są na tyle mało, że powinieneś być w stanie łatwo ich uniknąć. Zwrot + wartość w nowej linii? Czy ludzie naprawdę tak często robią?

Jakieś opinie? Czy używasz średników w javascript i dlaczego?


3
Nie działa dokładnie tak samo. Wstawianie średnika nie występuje wszędzie tam, gdzie nowa linia jest koniecznie w JavaScript. Zobacz drugi przykład na tej stronie Wikipedii . Średnik nie jest wstawiany tam, gdzie w tym przykładzie jest nowa linia.
Reid

1
Nie chodziło mi o to, że używanie średników i nieużywanie ich działało dokładnie tak samo, chodziło o to, że przypadki krawędzi w javascript i python były takie same. Z pewnością istnieje kilka skrajnych przypadków, w których musisz wiedzieć, co się dzieje. Najlepszy artykuł, jaki przeczytałem na ten temat: inimino.org/~inimino/blog/javascript_semicolons
Einar Egilsson

4
W JavaScript wstawiam średniki z tego samego powodu, dla którego używam kropek w zdaniach. Jasne, że tłumacz zazwyczaj rozumie twoje wypowiedzi bez nich, ale jest to po prostu zła forma.
JD Isaacks,

3
Możesz rozważyć napisanie poprawnego pytona w swoich przykładach. Wskaźnik komentarza to #nie `// '.
Aaron Dufour,

2
„jawne jest zawsze lepsze niż niejawne”

Odpowiedzi:


62

Powodem jest to, że w Pythonie znaki nowej linii są jednoznacznym sposobem oddzielania linii kodu; jest to zgodne z projektem, a sposób, w jaki to działa, został dokładnie przemyślany. W rezultacie kod Pythona jest doskonale czytelny i jednoznaczny bez żadnych specjalnych znaczników końca instrukcji (oprócz nowej linii).

Z kolei JavaScript został zaprojektowany z myślą o składni podobnej do C, w której instrukcje zawsze kończą się średnikiem. Aby uczynić język bardziej odpornym na błędy, próbuje zgadnąć, gdzie powinny znaleźć się dodatkowe średniki, aby poprawić kod. Ponieważ zostało to w pewnym sensie dostosowane do składni podobnej do C, nie zawsze działa zgodnie z oczekiwaniami (czasami interpreter skryptu zgaduje źle) i może stworzyć dość sprzeczny z intuicją kod. \

Lub argumentowanie w kategoriach „jawne jest lepsze niż niejawne”: w Pythonie nowa linia jest już całkowicie jawna, podczas gdy w JavaScript jest niejednoznaczna, więc dodajesz średnik, aby była wyraźna.


3
Aha, i możesz wstawiać kod w komentarzach, używając cudzysłowów.
tdammers,

1
Dobrym przykładem przypadku, w którym automatyczne wstawianie średnika zakończy się nieoczekiwanymi czynnościami jest: pastebin.com/aVeWGdya
HoLyVieR

5
W Pythonie reguły są dość proste: instrukcje kończą się przy podziale wierszy, chyba że istnieje niezamknięty ciąg wielowierszowy („” „,” ”), niezamknięty dykt ({}), niezamknięta lista ([]) lub odwrotny ukośnik natychmiast przed łamaniem linii. W javascript reguły są znacznie bardziej skomplikowane
Aaron Dufour,

5
Pokrycie 99% błędów to dobry sposób na pozostawienie tylko tych naprawdę trudnych do znalezienia. Można pozostawić je w Pythonie, ponieważ istnieją proste reguły obejmujące 100% problemów.
Aaron Dufour,

1
@Aaron: Zapomniałeś „niezamkniętego zestawu nawiasów (())”. (Nie jest to ściśle „niezamknięta krotka”, ponieważ w nawiasach nie stosuje się tylko krotek).
JAB

28

Sądzę, że jest dość zasadnicza różnica w sposobie działania w Pythonie. Cytując z posta Einara Egilssona powiązanego z: „średnik nie jest sugerowany na końcu linii, jeśli pierwszy token kolejnej linii może zostać przeanalizowany jako część tej samej instrukcji”.

W Pythonie podział wiersza zawsze kończy instrukcję, z wyjątkiem pewnych dość oczywistych przypadków, takich jak wyrażenie w nawiasach okrągłych. Z drugiej strony, JavaScript spróbuje parsować jak najwięcej wierszy, jak to możliwe, przed zakończeniem instrukcji, potencjalnie prowadząc do takich rzeczy:

// Define a function and name it area.
area = function(r) {
    return r * r * 3.14159
}

// Fooled you! We're actually invoking it.
(14)

8
Oto zabawny zwrot akcji. Zamień 14 na coś podobnego (a + 1) ? do_something() : do_something_else();i nagle obszar jest ustawiony na wartość zwracaną do_something()lub, do_something_else()a Ty pozostajesz bardzo zdezorientowany.
Reid,

20

Często minimalizuję moje pliki JS w trybie produkcyjnym. Oznacza, usuwanie komentarzy i podziałów wierszy.

Bez użycia średników spowodowałoby to uszkodzenie mojego Javascript.


8
OK, to prawda. Ale jeśli minimalizator jest rzeczywistym analizatorem składni, może w razie potrzeby wstawić go ponownie. Lub po prostu nie usuwaj łamania linii, => zachowujesz łamanie linii, tracisz średniki, są one mniej więcej tej samej liczbie, więc nic nie stracisz.
Einar Egilsson,

2
@Einar Egilsson Closure Compiler rzeczywiście to robi.
poważnedev

1
Wszystkie rzeczy są równe, nowa linia to ta sama liczba bajtów co średnik. Twój minimalizator może usuwać wszystkie nowe wiersze, ale wymaga obecności średników. To równa wymiana.
Logan Bailey,

3
@Logan: Zakłada się oczywiście, że są to nowe bajty ;-)
Cameron

1
Oczywiście, gdybyśmy użyli Pythona zamiast Javascript, musielibyśmy kodować WAY mniej, aby zrobić to samo, więc fakt, że średniki używają kilku bajtów mniej niż wcięcia, jest kwestią sporną.
BlueRaja - Danny Pflughoeft

5

To nie działa tak jak opisano.

JavaScript ma funkcję o nazwie Automatyczne wstawianie średnika, w której w zasadzie jeśli parser napotka niepoprawny token, a ostatni token przed nim był łamaniem linii, wówczas parser wstawi średnik tam, gdzie jest podział linii.

To jest źle. Przykład:

return
  1 + 2;

1jest całkowicie poprawnym tokenem, ale parser nadal wstawi średnik bezpośrednio po nim return.

Jak widzisz, nawet nie możesz dokładnie powiedzieć, gdzie nastąpi średnik.

Problem z automatycznym wstawianiem jest dwojaki:

  • Po pierwsze, ludzie mogą pominąć średnik, w którym automatyczne wstawianie nie może określić, czy należy wstawić.
  • Również średnik może zostać wstawiony tam, gdzie nie jest przeznaczony, jak wyżej.

Oczywiście użycie średnika po każdej instrukcji pomaga tylko przy pierwszym źródle błędów.

W każdym razie, jak się już domyślacie, uważam, że automatyczne wstawianie średnika w składni podobnej do C jest złym pomysłem.


1
Specyfikacja skryptu ECMA wyraźnie określa, w których przypadkach wstawiany jest średnik, więc wiersz „Nie możesz dokładnie powiedzieć, gdzie nastąpi średnik” jest niepoprawny. Problem polega na tym, że w niektórych przypadkach jest to nieintuicyjne, co utrudnia nauczenie kogoś, kto nie rozumie, jak to działa.
zzzzBov,

1
@zzzzBov: Tak, istnieje dokładna specyfikacja, ale czy ktoś naprawdę ma na myśli wszystkie przypadki podczas kodowania? Jesteś pewny? Programiści są leniwi i słusznie; nie chcą pamiętać skomplikowanej reguły, gdy zrobiłaby to znacznie prostsza. Starają się więc ominąć.
Svante,

Zgadzam się, że wstawienie średnika jest w dużej mierze niepotrzebne. Mówię tylko, że istnieje różnica między powiedzenie „nie masz pojęcia, gdzie średniki idą” i „spec do wstawiania średnik jest nieintuicyjne galimatias”
zzzzBov

1
@Svante: Ale przykład zwrotu pokazuje nam, że i tak musimy znać te zasady. Tam użyłeś średnika, ale to nie pomogło ci zrobić tego, co chciałeś. Biorąc pod uwagę, że język ma tę funkcję, mamy możliwość: (1) Pisanie średników wszędzie i znajomość zasad, więc rozumiemy, co się stanie (2) Nie pisanie średników wszędzie i rozumienie reguł, więc rozumiemy, co się stanie. Biorąc pod uwagę ten wybór, wolę pominąć średnik
Einar Egilsson,

4

Podam jeden prosty powód:

JavaScript wygląda na „trochę java-ish” lub „trochę c-ish”. Oczywiście jest to język dynamiczny, więc wygląda inaczej ... ale spójrz prawdzie w oczy - są aparaty ortodontyczne. Języki z nawiasami klamrowymi zwykle mają średniki. Naturalne refleksy uruchamiają się i sprawiają, że palec uderza w klawisz średnika, zanim uderzysz Enter.

Przeciwnie, Python nawet na pierwszy rzut oka wygląda zupełnie inaczej. W związku z tym intuicyjnie tworzy się niewielka lub żadna analogia do „standardowych nudnych języków”, a kiedy wchodzi się w „tryb python”, brak średników staje się naturalny.


2

Istnieje wiele dobrych powodów, aby nie używać wstawiania średników w JavaScript.

Przede wszystkim dlatego, że wstawienie średnika zgodnie z definicją w standardzie ECMAScript jest w niektórych przypadkach nieintuicyjne. @Svante wskazuje przypadek, w returnktórym użycie nowej linii spowoduje problemy.

Co mu się nie wspomina, że będzie to powodować problemy, jeśli jesteś przy użyciu średników jako dobrze, ponieważ wstawiania średnik dzieje, czy chcesz, czy nie.

Innym bardzo dobrym powodem, aby nie używać wstawiania średnika, jest kontrola wyjściowa. W wielu przypadkach JavaScript jest uruchamiany przez minifikator, zanim zostanie użyty w produkcji. Niektóre minizery potrafią obsłużyć automatyczne wstawianie średników, ale nie widzę powodu, aby polegać na tym, że działa idealnie

Dodatkowo w systemach zarządzania treścią wbudowany JavaScript może być automatycznie minimalizowany, a widziałem wiele przypadków, w których automatyczny minimalizator po prostu usuwa komentarze i przycina białe znaki (w tym znaki nowego wiersza) na początku i na końcu każdej linii.

Dla autorów, którzy nie mają wyboru, jakie narzędzia są wybierane, o wiele łatwiej jest po prostu trzymać się formatu, który działa w zdecydowanej większości przypadków.


Ach, przepraszam, ale czasowo swój trzeci akapit, mam zrobić wzmiankę, że w moim drugim do ostatniego zdania. :)
Svante,

Tak, problem z oprzyrządowaniem jest prawidłowy (chociaż dobre minizery powinny sobie z tym poradzić, np. Kompilator zamknięcia). Ale moim zdaniem i tak musimy znać te zasady, aby uniknąć takich rzeczy jak przykład „powrotu”. Gdy poznam zasady, równie dobrze mogę skorzystać z tej funkcji, zwłaszcza że sprawia ona, że ​​kod (IMO) jest bardziej czytelny.
Einar Egilsson

1

Nieużywanie średnika jest przepisem na niepowodzenie przy minimalizacji plików JavaScript. Dlatego się boję.


1

W Javascripcie możesz napisać program, który byłby poprawny pod względem składniowym przy braku automatycznego wstawiania średnika, a ASI przekształci ten program w inny program poprawny pod względem składniowym (na przykład przekształca kod, który zwraca wartość na kod, który nic nie zwraca). W Pythonie nie ma analogicznego przypadku. W Pythonie każdy znak nowej linii, który może zakończyć instrukcję , zakończy instrukcję, chyba że zostanie poprzedzony znakiem odwrotnego ukośnika. Technicznie przypuszczam, że reguły Javascript są równie deterministyczne, ale nie wiem, czy można streścić reguły Javascript dotyczące kończących instrukcji w jednym zdaniu.


1

W większości przypadków ASI JavaScript obsługuje rzeczy zgodnie z oczekiwaniami. Jednym z przykładów ASI może nie zachowujących się w oczekiwany sposób:

var i = 0

(function() {
   // do something
})()

Będzie to interpretowane jako wywołanie funkcji 0za pomocą funkcji anonimowej, a następnie wykonanie wyniku. W takim przypadku prawdopodobnie chciałeś wykonać zadanie, a następnie natychmiast wykonać funkcję anonimową.

Dla kogoś, kto nie zna ASI, może to być bardzo mylące, gdy napotkasz takie problemy, dlatego zawsze zalecam programistom w moim zespole stosowanie średników.

(Nawiasem mówiąc: nie używam średników podczas pracy nad projektami osobistymi / pobocznymi, ponieważ wiem, że nikt inny nie musiałby utrzymywać kodu).


1

Podobnie jak ty, myślę, że to trochę paranoiczne. Zasady wstawiania średników są dobrze zdefiniowane w JavaScript, podobnie jak w Pythonie i CoffeeScript. Nikt nie śmieszy Python ani CoffeeScript średnikami, więc dlaczego JavaScript jest traktowany inaczej?

Myślę, że to nadmierna reakcja na nieszczęsny stan typowego kodu JavaScript sprzed około dziesięciu lat - JavaScript był postrzegany jako słaby, błędny, brzydki, nie-dobry język. To było zawstydzenie. Nie można napisać dobrego kodu w JavaScript!

Następnie ludzie przyszedł i próbował udowodnić, że mógłby napisać piękny, jasny kod w JavaScripcie. Reguła „ zawsze używaj średników” była częścią tej fali. I szczerze mówiąc, może to uczynić kilka sytuacji nieco jaśniejszymi.

Dlaczego JavaScript jest nadal traktowany inaczej?

Jest bezwładność. I nie należy zapominać, że ludzie, którzy cenią sobie wyraźnie skonstruowany kod, często wolą języki w stylu C. Ludzie, którzy doceniają niejawnie ustrukturyzowany kod, często przechodzą na języki inne niż C (jak CoffeeScript).


0

Używam ich w JavaScript wyłącznie dla zachowania spójności. Jeśli większość linii ma

Python ma je dla przypadków skrajnych, takich jak wiele instrukcji w jednym wierszu, javascript ma je, a ponieważ znajdziesz je regularnie używane, jestem zgodny z normą, w której są używane.

Nie mogę znaleźć zastosowania dla wielu instrukcji w tym samym wierszu i dlatego nie przewiduję używania średników.


Tak, naprawiłem przykład w Pythonie. Ale kwestia pozostaje, Python ma również średniki, możesz wstawiać je po każdej instrukcji (i musisz, jeśli masz więcej niż jeden w każdej linii), ale ludzie ich nie używają.
Einar Egilsson,

0

Jeśli użyjesz czegoś takiego jak bundle-fu i menedżer zasobów dla swojej aplikacji sieciowej w szynach, to okropnie się zepsuje, jeśli nie napotka średnika na końcu tokena w javascript. Dobrym zwyczajem jest więc postawienie takiego.


Cóż, wielka trójka, YUI Compressor, Closure Compiler i UglifyJS, wszystkie wstawiają średniki. Nie dziwię się, że rubinowy port JSMin ma problemy.
Benjamin Atkin,

0

Nie pamiętam, która dokładnie wersja IE, ale w niektórych przypadkach IE dosłownie popełni błąd, jeśli brakuje średnika. IIRC ma miejsce, gdy masz w zasięgu globalnym coś takiego:

var myFunc = function() {
  ...
}

Jeśli nie dodasz; po nawiasie zamykającym program faktycznie zawiedzie w niektórych wersjach IE. To, wraz z innymi powodami (w tym zaleceniem Crockforda, aby zawsze używać ich jawnie), skłoniło mnie do tego, aby zawsze używać ich wyraźnie w każdym przypadku.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.