Dlaczego nie ma ++
i --
operatorów w Pythonie?
Dlaczego nie ma ++
i --
operatorów w Pythonie?
Odpowiedzi:
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.
i
bezpośrednio - jeśli naprawdę potrzebujesz i nie możesz po prostu np. użyćarray.append()
i++
i ++i
...
++
i --
używanego w sposób, który skutkuje nieokreślonym lub nieokreślonym zachowanie. Umożliwiają pisanie skomplikowanego, trudnego do poprawnej analizy kodu.
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 *++p
i *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.
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.
one--
jest zero?
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.
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:
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.
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!
int
są niezmienne. W int
C po prostu oznacza miejsce w pamięci. A bity w tym miejscu są bardzo zmienne. Możesz na przykład utworzyć odniesienie int
i zmienić odniesienie do tego odniesienia. Ta zmiana jest widoczna we wszystkich odniesieniach (w tym pierwotnej int
zmiennej) do tego miejsca. To samo nie dotyczy obiektu liczb całkowitych w języku Python.
W Pythonie chodzi o przejrzystość i żaden programista nie odgadnie poprawnie znaczenia, --a
chyba ż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).
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 a
wskazuje teraz na inny obiekt jako zmienne b
i c
. Ponieważ używałeś, a = a + 1
jest 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 a
nowego obiektu, ale zwiększysz stary. Wszystkie te rzeczy są IMHO dla minimalizacji zamieszania. Aby lepiej zrozumieć, zobacz jak działają zmienne Pythona:
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
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.
return a[i++]
z return a[i=i+1]
.
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.
x++
jest bliżej x += 1
niż x = x + 1
, ponieważ te dwa robią różnicę również na obiektach zmiennych.
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ć.
Uważam, że wynika z wyznania Pythona, że „jawne jest lepsze niż niejawne”.
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.
++
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 ++.
f(a)
gdzie a
jest lista, jakiś niezmienny obiekt.
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.
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.
++
różni += 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ę.
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.
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.
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.
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()) :