Dlaczego w Pythonie nie ma operatorów ++ i -?


Odpowiedzi:


443

Nie dlatego, że to nie ma sensu; sensowne jest zdefiniowanie „x ++” jako „x + = 1, zwracając uwagę na poprzednie wiązanie x”.

Jeśli chcesz poznać pierwotny powód, musisz albo przebrnąć przez stare listy mailingowe w Pythonie, albo zapytać kogoś, kto tam był (np. Guido), ale łatwo to uzasadnić po fakcie:

Proste zwiększanie i zmniejszanie nie jest tak potrzebne jak w innych językach. Nie piszesz for(int i = 0; i < 10; ++i)często takich rzeczy jak w Pythonie; zamiast tego robisz rzeczy takie jak for i in range(0, 10).

Ponieważ nie jest potrzebny tak często, istnieje znacznie mniej powodów, aby nadać mu własną specjalną składnię; kiedy trzeba zwiększyć, +=zwykle jest w porządku.

To nie jest decyzja, czy ma to sens, czy można to zrobić - robi i może. To pytanie, czy warto dodać tę korzyść do podstawowej składni języka. Pamiętaj, że są to cztery operatory - postinc, postdec, preinc, predec i każdy z nich musiałby mieć własne przeciążenia klasy; wszystkie muszą zostać sprecyzowane i przetestowane; dodawałby do kodu opcodes (co oznacza większy, a zatem wolniejszy silnik VM); każda klasa, która obsługuje przyrost logiczny, musiałaby je zaimplementować (na górze +=i -=).

To wszystko jest zbędny z +=a -=, tak by stała się ona stratę netto.


98
Często przydatne jest użycie czegoś takiego jak tablica [i ++], co nie jest uporządkowane za pomocą + = / - =.
Turner Hayes

102
@thayes: To nie jest powszechny wzorzec w Pythonie.
Glenn Maynard

9
@thayes Ponieważ będzie to w pętli, równie dobrze możesz zapętlić ibezpośrednio - jeśli naprawdę potrzebujesz i nie możesz po prostu np. użyćarray.append()
Tobias Kienzler

31
Widzę o wiele większy problem związany z czytelnością i przewidywalnością. Powrót w moich dni C, widziałem więcej niż wystarczająco dużo błędów wynikających z nieporozumień na temat rozróżnienia między i++i ++i...
Charles Duffy

5
Dodając do uzasadnienia po fakcie: w projekcie, nad którym pracuję, spotkałem (bardziej niż ktokolwiek powinien w życiu) sporą ilość kodu C ++ cierpiącego na problemy ++i --używanego w sposób, który skutkuje nieokreślonym lub nieokreślonym zachowanie. Umożliwiają pisanie skomplikowanego, trudnego do poprawnej analizy kodu.
Brian Vandenberg,

84

Ta oryginalna odpowiedź, którą napisałem, jest mitem z folkloru komputerowego : zdemaskowany przez Dennisa Ritchiego jako „historycznie niemożliwy”, jak zauważono w listach do redakcji Komunikacji ACM z lipca 2012 r. Doi: 10.1145 / 2209249.2209251


Operatory inkrementacji / dekrementacji C zostały wynalezione w czasie, gdy kompilator C nie był zbyt inteligentny, a autorzy chcieli mieć możliwość określenia bezpośredniego zamiaru użycia operatora języka maszynowego, który zaoszczędził garść cykli dla kompilatora, który może zrobić

load memory
load 1
add
store memory

zamiast

inc memory 

a PDP-11 obsługiwał nawet instrukcje „autoinkrementacji” i „autoinkrementacji odroczonej” odpowiadające odpowiednio *++pi *p++. Jeśli okropnie ciekawy, patrz rozdział 5.3 instrukcji .

Ponieważ kompilatory są wystarczająco inteligentne, aby poradzić sobie z wysokopoziomowymi sztuczkami optymalizacyjnymi wbudowanymi w składnię C, są teraz tylko wygodą składniową.

Python nie ma sztuczek, aby przekazać intencje asemblerowi, ponieważ nie używa żadnego.


4
JavaScript ma ++. Nie sądzę, że jest to „sztuczka w przekazywaniu intencji asemblerowi”. Ponadto Python ma kod bajtowy. Myślę więc, że powodem jest coś innego.
Nathan Davis,

13
To „dostarczanie wskazówek kompilatorowi” jest rzeczywiście mitem. Szczerze mówiąc, jest głupim dodatkiem do dowolnego języka i narusza następujące dwie zasady: 1. Nie kodujesz, aby komputer mógł czytać, kodujesz dla innego inżyniera do czytania. I 2. Nie kodujesz dla kompetentnego inżyniera do czytania, kodujesz dla kompetentnego inżyniera do czytania, gdy jestem wyczerpany o 3 nad ranem i wskoczyłeś na kofeinę.

3
@ tgm1024 Aby być uczciwym, kodując 10–30 znaków na sekundę w trybie półdupleksowym, kodujesz, abyś mógł wprowadzić go przed następnym tygodniem.
msw

4
@ Tgm1024 Unix i C zobaczyli większość swojego początkowego rozwoju na PDP-11, które wykorzystywały niewiarygodnie wolne teletypy do komunikacji z użytkownikiem. Chociaż masz całkowitą rację, że dzisiejsze kodowanie maszyny jest w większości głupie, to wtedy wąskim gardłem był interfejs człowiek / maszyna. Trudno wyobrazić sobie pracę tak wolno, jeśli nigdy nie musiałeś.
msw

65

Zawsze zakładałem, że ma to związek z tą linią zen Pythona:

Powinien być jeden - a najlepiej tylko jeden - oczywisty sposób na zrobienie tego.

x ++ i x + = 1 robią dokładnie to samo, więc nie ma powodu, aby mieć oba.


10
one--jest zero?
Andre Holzner,

25
one--jest jednym w zdaniu, ale zaraz po nim zero. Tak więc ten „koan” wskazuje również, że operatory inkrementacji / dekrementacji nie są oczywiste.
Victor K

14
@EralpB Jeśli usuniesz + =, nie będziesz mógł robić rzeczy takich jak x + = 10. + = to bardziej ogólny przypadek ++
Rory

8
Ponadto: „Jawne jest lepsze niż niejawne”.
Ber

2
Zdecydowanie nie to samo, ponieważ x + = 1 NIE jest wyrażeniem - jest wyrażeniem - i niczego nie ocenia. Nie możesz robić takich rzeczy jak: 'row [col ++] = a; wiersz [col ++] = b '. Nie wspominając o rzeczach przed i po dodaniu, które ma c ++.
Uri

38

Oczywiście moglibyśmy powiedzieć „Guido właśnie tak postanowił”, ale myślę, że tak naprawdę chodzi o powody tej decyzji. Myślę, że jest kilka powodów:

  • Łączy ze sobą wypowiedzi i wyrażenia, co nie jest dobrą praktyką. Zobacz http://norvig.com/python-iaq.html
  • Ogólnie zachęca ludzi do pisania mniej czytelnego kodu
  • Dodatkowa złożoność implementacji języka, która nie jest konieczna w Pythonie, jak już wspomniano

12
Cieszę się, że ktoś w końcu wspomniał o aspekcie stwierdzenia vs. Przypisanie w C jest wyrażeniem, a więc operatorem ++. W Python przypisanie jest instrukcją, więc gdyby miał ++, prawdopodobnie musiałby to być także instrukcja przypisania (a nawet mniej przydatna lub potrzebna).
martineau,

Zgadzam się - jeśli byłyby to oświadczenia, to przynajmniej byłoby absolutnie bez sensu mówić o różnicy między postoperatorami a preoperatorami.
Crowman

17

Ponieważ w Pythonie liczby całkowite są niezmienne (int's + = faktycznie zwraca inny obiekt).

Ponadto w ++ / - musisz martwić się o przyrost / spadek przed i po, a zapisanie zajmuje tylko jedno naciśnięcie klawisza x+=1. Innymi słowy, pozwala uniknąć potencjalnego zamieszania kosztem bardzo małego zysku.


ints są również niezmienne w C. Jeśli tak nie uważasz, postaraj się, aby kompilator C wygenerował kod dla 42++... Coś takiego (modyfikacja stałej literalnej) było w rzeczywistości możliwe w niektórych starych kompilatorach Fortrana (a przynajmniej tak czytałem): Wszystkie przyszłe zastosowania tego dosłowności w tym przebiegu programu miałoby wtedy naprawdę inną wartość. Miłego debugowania!
Lutz Prechelt

10
Dobrze. 42 jest dosłowną stałą . Stałe są (a przynajmniej powinny być) niezmienne. Nie oznacza to, że Cs intsą niezmienne. W intC po prostu oznacza miejsce w pamięci. A bity w tym miejscu są bardzo zmienne. Możesz na przykład utworzyć odniesienie inti zmienić odniesienie do tego odniesienia. Ta zmiana jest widoczna we wszystkich odniesieniach (w tym pierwotnej intzmiennej) do tego miejsca. To samo nie dotyczy obiektu liczb całkowitych w języku Python.
Nathan Davis

x + = 1 nie staje się inc mem. Python wykonuje wywołanie funkcji, aby dodać 1 do zmiennej; to jest żałosne.
PalaDolphin

12

Przejrzystość!

W Pythonie chodzi o przejrzystość i żaden programista nie odgadnie poprawnie znaczenia, --achyba że nauczy się języka o takiej konstrukcji.

W Pythonie chodzi również o unikanie konstrukcji, które zapraszają do błędów, a ++operatorzy są znanymi bogatymi źródłami błędów. Te dwa powody są wystarczające, aby nie mieć tych operatorów w Pythonie.

Decyzja, że ​​Python używa wcięcia do oznaczania bloków, a nie środków syntaktycznych, takich jak pewna forma nawiasów początkowych / końcowych lub obowiązkowe oznaczanie końca, opiera się w dużej mierze na tych samych względach.

Na przykład, spójrz na dyskusję dotyczącą wprowadzenia operatora warunkowego (w C cond ? resultif : resultelse:) do Pythona w 2005 roku. Przeczytaj przynajmniej pierwszą wiadomość i wiadomość decyzyjną tej dyskusji (która wcześniej miała kilka prekursorów na ten sam temat).

Ciekawostki: Często wymienianym w nim PEP jest „Propozycja rozszerzenia Pythona” PEP 308 . LC oznacza zrozumienie listy , GE oznacza wyrażenie generatora (i nie martw się, jeśli ci się mylą, nie są to żadne z niewielu skomplikowanych miejsc Pythona).


10

Rozumiem, dlaczego Python nie ma ++ operatora, brzmi następująco: Kiedy napiszesz to w pythonie a=b=c=1, otrzymasz trzy zmienne (etykiety) wskazujące na ten sam obiekt (którego wartość wynosi 1). Możesz to sprawdzić za pomocą funkcji id, która zwróci adres pamięci obiektu:

In [19]: id(a)
Out[19]: 34019256

In [20]: id(b)
Out[20]: 34019256

In [21]: id(c)
Out[21]: 34019256

Wszystkie trzy zmienne (etykiety) wskazują na ten sam obiekt. Teraz zwiększ jedną ze zmiennych i zobacz, jak wpływa ona na adresy pamięci:

In [22] a = a + 1

In [23]: id(a)
Out[23]: 34019232

In [24]: id(b)
Out[24]: 34019256

In [25]: id(c)
Out[25]: 34019256

Widać, że zmienna awskazuje teraz na inny obiekt jako zmienne bi c. Ponieważ używałeś, a = a + 1jest to wyraźnie jasne. Innymi słowy, przypisujesz całkowicie inny obiekt do etykietya . Wyobraź sobie, że możesz to napisać a++, co sugerowałoby, że nie przypisałeś zmiennego anowego obiektu, ale zwiększysz stary. Wszystkie te rzeczy są IMHO dla minimalizacji zamieszania. Aby lepiej zrozumieć, zobacz jak działają zmienne Pythona:

Dlaczego w Pythonie funkcja może modyfikować niektóre argumenty postrzegane przez program wywołujący, a inne nie?

Czy Python ma funkcję call-by-value lub call-by-reference? Ani.

Czy Python przekazuje wartość, czy referencję?

Czy Python jest przekazywany przez odniesienie czy przekazywany przez wartość?

Python: Jak przekazać zmienną przez referencję?

Zrozumienie zmiennych Python i zarządzania pamięcią

Emulowanie zachowania wartości dodanej w pythonie

Funkcje języka Python wywoływane są przez odniesienie

Kod jak Pythonista: Idiomatic Python


9

Zostało tak zaprojektowane. Operatory inkrementacji i dekrementacji to tylko skróty x = x + 1. Python zazwyczaj przyjmuje strategię projektowania, która zmniejsza liczbę alternatywnych sposobów wykonywania operacji. Rozszerzone przypisanie jest najbliższą rzeczą do zwiększania / zmniejszania operatorów w Pythonie i nie zostały nawet dodane do Pythona 2.0.


3
Tak kolego, jak, można wymienić return a[i++]z return a[i=i+1].
Luís de Sousa,

7

Jestem bardzo nowy w Pythonie, ale podejrzewam, że powodem jest nacisk na zmienne i niezmienne obiekty w języku. Teraz wiem, że x ++ można łatwo interpretować jako x = x + 1, ale WYGLĄDA, jakbyś inkrementował na miejscu obiekt, który mógłby być niezmienny.

Tylko moje zgadywanie / przeczucie.


W tym aspekcie x++jest bliżej x += 1niż x = x + 1, ponieważ te dwa robią różnicę również na obiektach zmiennych.
glglgl 13.12.12

4

Po pierwsze, na Python wpływ ma jedynie C; jest pod silnym wpływem ABC , który najwyraźniej nie ma tych operatorów , więc nie powinno być zaskoczeniem, że nie można ich znaleźć w Pythonie.

Po drugie, jak powiedzieli inni, przyrost i spadek są wspierane przez +=i-= już.

Po trzecie, pełne wsparcie dla ++a-- zestawu operatora operatora zwykle obejmuje obsługę zarówno ich wersji przedrostkowej, jak i późniejszej. W C i C ++ może to prowadzić do wszelkiego rodzaju „uroczych” konstrukcji, które wydają mi się (w moim przekonaniu) sprzeczne z duchem prostoty i bezpośredniości, którą obejmuje Python.

Na przykład, podczas gdy instrukcja C while(*t++ = *s++);może wydawać się prosta i elegancka dla doświadczonego programisty, dla kogoś, kto się jej uczy, jest ona prosta. Wrzuć mieszankę przyrostków i przyrostków przedrostków i przyrostków, a nawet wielu profesjonalistów będzie musiało się zatrzymać i trochę pomyśleć.


3

Uważam, że wynika z wyznania Pythona, że ​​„jawne jest lepsze niż niejawne”.


Cóż, nie piszesz jawnie instrukcji „start” i „end” w Pythonie, prawda? Chociaż zgadzam się z tym stwierdzeniem, myślę, że istnieją granice tego. Chociaż możemy kłócić się o te granice, myślę, że wszyscy możemy się zgodzić, że istnieje linia, której przekraczanie jest niepraktyczne. A ponieważ jest tak wiele opinii i uzasadnień tej decyzji, nie sądzę, by był to jasny wybór. Przynajmniej nie mogę znaleźć źródła, w którym jest to wyraźnie stwierdzone
Hasan Ammori,

3

Może to wynikać z tego, że @GlennMaynard patrzy na tę sprawę w porównaniu z innymi językami, ale w Pythonie robisz rzeczy w taki sposób. To nie jest pytanie „dlaczego”. Jest tam i możesz robić rzeczy z tym samym efektem x+=. W Zen of Python podano: „powinien być tylko jeden sposób rozwiązania problemu”. Wiele możliwości wyboru jest świetnych w sztuce (wolność wypowiedzi), ale kiepska w inżynierii.


2

++Klasa operatorów są wyrażenia z efektami ubocznymi. Zasadniczo nie można tego znaleźć w Pythonie.

Z tego samego powodu przypisanie nie jest wyrażeniem w Pythonie, co zapobiega wspólnemu if (a = f(...)) { /* using a here */ }idiomowi.

Wreszcie podejrzewam, że operator nie jest zbyt spójny z semantyką odwołań do Pythona. Pamiętaj, że Python nie ma zmiennych (lub wskaźników) z semantyką znaną z C / C ++.


1
nic nie stoi na przeszkodzie, aby wywołać funkcję z efektem ubocznym w rozumieniu testu / wyrażenia / listy: f(a)gdzie ajest lista, jakiś niezmienny obiekt.
Jean-François Fabre

1

Być może lepszym pytaniem byłoby pytanie, dlaczego operatorzy ci istnieją w C. K&R wzywa operatorów inkrementacji i dekrementacji do „niezwykłych” (sekcja 2.8 na stronie 46). Wprowadzenie nazywa je „bardziej zwięzłym i często bardziej wydajnym”. Podejrzewam, że fakt, że operacje te zawsze pojawiają się w manipulacji wskaźnikami, również odegrał rolę w ich wprowadzeniu. W Pythonie prawdopodobnie zdecydowano, że nie ma sensu próbować optymalizować przyrostów (w rzeczywistości właśnie wykonałem test w C i wydaje się, że zestaw wygenerowany przez gcc używa addl zamiast inc w obu przypadkach) i nie ma wskaźnik arytmetyczny; więc byłby to tylko jeden sposób na zrobienie tego i wiemy, że Python tego nie znosi.


1

tak jak to zrozumiałem, więc nie pomyślisz, że wartość w pamięci się zmieniła. in c, gdy wykonasz x ++, wartość x w pamięci zmienia się. ale w pythonie wszystkie liczby są niezmienne, stąd adres wskazany przez x jako wciąż ma x nie x + 1. kiedy piszesz x ++, pomyślałbyś, że x zmienia to, co się naprawdę dzieje, polega na tym, że x referencja jest zmieniana na lokalizację w pamięci, w której przechowywany jest x + 1, lub odtwórz tę lokalizację, jeśli nie ma.


1
Czym więc to się ++różni += 1?
Ber

1

Aby wypełnić i tak dobre odpowiedzi na tej stronie:

Załóżmy, że zdecydujemy się to zrobić, prefiks ( ++i), który złamie jednoargumentowe operatory + i -.

Dzisiaj prefiksowanie przez ++lub --nic nie robi, ponieważ umożliwia dwukrotny operator jednoargumentowy plus (nic nie robi) lub dwukrotny unarny minus (dwa razy: sam się anuluje)

>>> i=12
>>> ++i
12
>>> --i
12

To potencjalnie złamałoby tę logikę.


1

Inne odpowiedzi opisują, dlaczego iteratory nie są potrzebne, ale czasami jest to przydatne podczas przypisywania w celu zwiększenia zmiennej w linii, możesz osiągnąć ten sam efekt za pomocą krotek i wielokrotnego przypisania:

b = ++a staje się:

a,b = (a+1,)*2

i b = a++staje się:

a,b = a+1, a

Pyton 3,8 wprowadza zadanie :=operatora, co pozwala na uzyskanie foo(++a)z

foo(a:=a+1)

foo(a++) jest jednak wciąż nieuchwytny.


2
: = przypisanie jest hańbą
nehem

0

Myślę, że odnosi się to do koncepcji zmienności i niezmienności przedmiotów. 2,3,4,5 są niezmienne w pythonie. Zobacz zdjęcie poniżej. 2 ma ustalony identyfikator do czasu tego procesu w Pythonie.

Identyfikator stałych i zmiennych

x ++ zasadniczo oznacza przyrost lokalny, taki jak C. W C, x ++ wykonuje przyrost lokalny. Zatem x = 3, a x ++ zwiększyłoby 3 w pamięci do 4, w przeciwieństwie do Pythona, gdzie 3 nadal istniałoby w pamięci.

Dlatego w Pythonie nie trzeba odtwarzać wartości w pamięci. Może to prowadzić do optymalizacji wydajności.

To odpowiedź oparta na przeczuciu.


0

Wiem, że jest to stary wątek, ale nie obejmuje go najczęstszy przypadek użycia ++ i, ponieważ jest to ręczne indeksowanie zestawów, gdy nie ma dostępnych indeksów. Ta sytuacja powoduje, że Python zapewnia enumerate ()

Przykład: w dowolnym języku, gdy używasz konstrukcji takiej jak foreach, aby iterować po zestawie - dla przykładu powiemy nawet, że jest to zestaw nieuporządkowany i potrzebujesz unikalnego indeksu, aby wszystko rozróżnić, powiedz

i = 0
stuff = {'a': 'b', 'c': 'd', 'e': 'f'}
uniquestuff = {}
for key, val in stuff.items() :
  uniquestuff[key] = '{0}{1}'.format(val, i)
  i += 1

W takich przypadkach python zapewnia metodę wyliczania, np

for i, (key, val) in enumerate(stuff.items()) :
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.