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.
ibezpoś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 *++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.
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!
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.
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).
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:
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 += 1niż 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 ajest 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()) :