Perché non ci sono ++
e --
operatori in Python?
Perché non ci sono ++
e --
operatori in Python?
Risposte:
Non è perché non ha senso; ha perfettamente senso definire "x ++" come "x + = 1, valutando l'associazione precedente di x".
Se vuoi conoscere il motivo originale, dovrai sfogliare le vecchie mailing list di Python o chiedere a qualcuno che era lì (ad esempio Guido), ma è abbastanza facile giustificare dopo il fatto:
Semplice incremento e decremento non sono necessari tanto quanto in altre lingue. Non scrivi cose come for(int i = 0; i < 10; ++i)
in Python molto spesso; invece fai cose come for i in range(0, 10)
.
Dato che non è necessario quasi altrettanto spesso, ci sono molte meno ragioni per dargli la sua sintassi speciale; quando è necessario incrementare, di +=
solito va bene.
Non è una decisione se ha senso o se può essere fatto - lo fa e può farlo. È una questione se vale la pena aggiungere il vantaggio alla sintassi principale della lingua. Ricorda, si tratta di quattro operatori: postinc, postdec, preinc, predec e ognuno di questi dovrebbe avere i propri sovraccarichi di classe; tutti devono essere specificati e testati; aggiungerebbe codici operativi al linguaggio (implicando un motore VM più grande, e quindi più lento); ogni classe che supporta un incremento logico dovrebbe implementarli (sopra +=
e -=
).
Tutto ciò è ridondante con +=
e -=
, quindi diventerebbe una perdita netta.
i
direttamente - se ne hai davvero bisogno e non puoi semplicemente usarloarray.append()
i++
e ++i
...
++
e --
veniva usato in modi che risultano indefiniti o non specificati comportamento. Consentono di scrivere codice complicato, difficile da analizzare correttamente.
Questa risposta originale che ho scritto è un mito del folklore dell'informatica : ridimensionato da Dennis Ritchie come "storicamente impossibile" come indicato nelle lettere agli editori di Communications of the ACM July 2012 doi: 10.1145 / 2209249.2209251
Gli operatori di incremento / decremento C sono stati inventati in un momento in cui il compilatore C non era molto intelligente e gli autori volevano essere in grado di specificare l'intenzione diretta di utilizzare un operatore di linguaggio macchina che ha salvato una manciata di cicli per un compilatore che potrebbe fare un
load memory
load 1
add
store memory
invece di
inc memory
e il PDP-11 supportava anche le istruzioni "autoincremento" e "autoincremento differito" corrispondenti rispettivamente a *++p
e *p++
. Vedere la sezione 5.3 del manuale se orribilmente curioso.
Poiché i compilatori sono abbastanza intelligenti da gestire i trucchi di ottimizzazione di alto livello integrati nella sintassi di C, ora sono solo una comodità sintattica.
Python non ha trucchi per comunicare le intenzioni all'assemblatore perché non ne usa una.
Ho sempre pensato che avesse a che fare con questa linea dello zen di Python:
Dovrebbe esserci uno - e preferibilmente solo uno - un modo ovvio per farlo.
x ++ e x + = 1 fanno esattamente la stessa cosa, quindi non c'è motivo di avere entrambi.
one--
è zero?
one--
è uno nella frase, ma zero immediatamente dopo. Quindi questo 'koan' suggerisce anche che gli operatori di incremento / decremento non sono ovvi.
Certo, potremmo dire "Guido ha deciso in quel modo", ma penso che la domanda riguardi davvero le ragioni di quella decisione. Penso che ci siano diverse ragioni:
Perché, in Python, i numeri interi sono immutabili (int's + = in realtà restituisce un oggetto diverso).
Inoltre, con ++ / - devi preoccuparti di pre-post-incremento / decremento e ci vuole solo un altro tasto per scrivere x+=1
. In altre parole, evita la potenziale confusione a scapito di un guadagno molto scarso.
42++
... Qualcosa del genere (modificando una costante letterale) era effettivamente possibile in alcuni vecchi compilatori Fortran (o così ho letto): tutti gli usi futuri di quel letterale in quel programma avrebbe davvero un valore diverso. Buon debug!
int
in generale siano immutabili. Un int
in C indica semplicemente un posto nella memoria. E i bit in quel posto sono molto mutabili. Ad esempio, è possibile creare un riferimento di an int
e modificare il riferimento di quel riferimento. Questa modifica è visibile in tutti i riferimenti (inclusa la int
variabile originale ) in quel luogo. Lo stesso non vale per un oggetto intero Python.
Python è molto incentrato sulla chiarezza e nessun programmatore è in grado di indovinare correttamente il significato a --a
meno che non abbia imparato una lingua con quel costrutto.
Python si occupa anche di evitare costrutti che invitano errori e gli ++
operatori sono noti per essere ricche fonti di difetti. Questi due motivi sono sufficienti per non avere quegli operatori in Python.
La decisione che Python utilizzi il rientro per contrassegnare i blocchi anziché i mezzi sintattici come una qualche forma di bracketing iniziale / finale o marcatura finale obbligatoria si basa in gran parte sulle stesse considerazioni.
Per esempio, dai un'occhiata alla discussione sull'introduzione di un operatore condizionale (in C cond ? resultif : resultelse
:) in Python nel 2005. Leggi almeno il primo messaggio e il messaggio di decisione di quella discussione (che aveva avuto diversi precursori sullo stesso argomento in precedenza).
Curiosità: il PEP menzionato di frequente in esso è la "Proposta di estensione Python" PEP 308 . LC significa comprensione della lista , GE significa espressione del generatore (e non preoccuparti se quelli ti confondono, non sono nessuno dei pochi punti complicati di Python).
La mia comprensione del motivo per cui python non ha un ++
operatore è la seguente: quando scrivi questo in python a=b=c=1
otterrai tre variabili (etichette) che puntano allo stesso oggetto (il cui valore è 1). È possibile verificarlo utilizzando la funzione id che restituirà un indirizzo di memoria dell'oggetto:
In [19]: id(a)
Out[19]: 34019256
In [20]: id(b)
Out[20]: 34019256
In [21]: id(c)
Out[21]: 34019256
Tutte e tre le variabili (etichette) puntano allo stesso oggetto. Ora incrementa una delle variabili e vedi come influenza gli indirizzi di memoria:
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
a
Ora puoi vedere che la variabile punta a un altro oggetto come variabili b
e c
. Perché l'hai usato a = a + 1
è esplicitamente chiaro. In altre parole, assegni completamente un altro oggetto all'etichetta a
. Immagina di poterlo scrivere a++
, suggerirei di non aver assegnato a un a
nuovo oggetto variabile ma di incrementare il vecchio. Tutto questo è IMHO per ridurre al minimo la confusione. Per una migliore comprensione, vedi come funzionano le variabili Python:
Python è call-by-value o call-by-reference? Nessuno dei due.
Python passa per valore o per riferimento?
Python è pass-by-reference o pass-by-value?
Python: come faccio a passare una variabile per riferimento?
Comprensione delle variabili Python e della gestione della memoria
Emulazione del comportamento pass-by-value in Python
È stato progettato in questo modo. Gli operatori di incremento e decremento sono solo scorciatoie per x = x + 1
. Python ha in genere adottato una strategia di progettazione che riduce il numero di mezzi alternativi per eseguire un'operazione. L'assegnazione aumentata è la cosa più vicina agli operatori di incremento / decremento in Python e non sono stati aggiunti nemmeno fino a Python 2.0.
return a[i++]
con return a[i=i+1]
.
Sono molto nuovo di Python ma sospetto che la ragione sia dovuta all'enfasi tra oggetti mutabili e immutabili all'interno del linguaggio. Ora so che x ++ può essere facilmente interpretato come x = x + 1, ma sembra che tu stia incrementando sul posto un oggetto che potrebbe essere immutabile.
Solo la mia ipotesi / sensazione / impressione.
x++
è più vicino x += 1
che a x = x + 1
, questi due fanno la differenza anche su oggetti mutabili.
Primo, Python è influenzato solo indirettamente da C; è fortemente influenzato da ABC , che apparentemente non ha questi operatori , quindi non dovrebbe essere una grande sorpresa non trovarli nemmeno in Python.
In secondo luogo, come altri hanno già detto, l'incremento e il decremento sono supportati da +=
e -=
già.
In terzo luogo, il supporto completo per un set di operatori ++
e di --
solito include il supporto sia della versione prefissata che postfissa. In C e C ++, questo può portare a tutti i tipi di costrutti "adorabili" che sembrano (per me) contrari allo spirito di semplicità e immediatezza che Python abbraccia.
Ad esempio, mentre l'affermazione C while(*t++ = *s++);
può sembrare semplice ed elegante per un programmatore esperto, per qualcuno che la impara, è tutt'altro che semplice. Aggiungete un mix di incrementi e decrementi di prefisso e postfisso e anche molti professionisti dovranno fermarsi e pensare un po '.
Credo che derivi dal credo di Python che "esplicito è meglio che implicito".
Ciò può essere dovuto al fatto che @GlennMaynard sta esaminando la questione come in confronto con altre lingue, ma in Python, fai le cose in modo pitone. Non è una domanda "perché". È lì e puoi fare le cose con lo stesso effetto x+=
. In The Zen of Python , viene fornito: "dovrebbe esserci un solo modo per risolvere un problema". Le scelte multiple sono grandi nell'arte (libertà di espressione) ma pessime nell'ingegneria.
La ++
classe di operatori sono espressioni con effetti collaterali. Questo è qualcosa che generalmente non si trova in Python.
Per lo stesso motivo un incarico non è un'espressione in Python, impedendo così il comune if (a = f(...)) { /* using a here */ }
linguaggio .
Infine, sospetto che l'operatore non sia molto coerente con la semantica di riferimento di Pythons. Ricorda, Python non ha variabili (o puntatori) con la semantica conosciuta da C / C ++.
f(a)
dov'è a
una lista, qualche oggetto immutabile.
Forse una domanda migliore sarebbe porre perché questi operatori esistono in C. K&R definisce gli operatori di incremento e decremento "insoliti" (Sezione 2.8pag. 46). L'introduzione li definisce "più concisi e spesso più efficienti". Sospetto che anche il fatto che queste operazioni emergano sempre nella manipolazione dei puntatori abbia avuto un ruolo nella loro introduzione. In Python è stato probabilmente deciso che non aveva senso tentare di ottimizzare gli incrementi (in effetti ho appena fatto un test in C, e sembra che l'assembly generato da gcc usi addl anziché incl in entrambi i casi) e non c'è aritmetica del puntatore; quindi sarebbe stato solo un altro modo per farlo e sappiamo che Python lo detesta.
come ho capito, quindi non penserai che il valore in memoria sia cambiato. in c quando si esegue x ++ il valore di x in memoria cambia. ma in Python tutti i numeri sono immutabili quindi l'indirizzo che x ha indicato come ancora ha x non x + 1. quando scrivi x ++ pensi che x cambi ciò che realmente accade è che x refrence viene cambiato in una posizione in memoria in cui x + 1 è memorizzato o ricrea questa posizione se non esiste.
++
e diverso da += 1
?
Per completare già buone risposte su quella pagina:
Supponiamo di decidere di fare questo, prefisso ( ++i
) che spezzerebbe gli operatori + e - unari.
Oggi, prefisso ++
o --
non fa nulla, perché consente all'operatore unario più due volte (non fa nulla) o meno unario due volte (due volte: si annulla)
>>> i=12
>>> ++i
12
>>> --i
12
Quindi ciò potrebbe potenzialmente infrangere quella logica.
Altre risposte hanno descritto il motivo per cui non è necessario per gli iteratori, ma a volte è utile quando si assegna per aumentare una variabile in linea, è possibile ottenere lo stesso effetto utilizzando tuple e assegnazioni multiple:
b = ++a
diventa:
a,b = (a+1,)*2
e b = a++
diventa:
a,b = a+1, a
Python 3.8 introduce l' :=
operatore di assegnazione , permettendoci di ottenere foo(++a)
con
foo(a:=a+1)
foo(a++)
è comunque sfuggente.
Penso che ciò si riferisca ai concetti di mutabilità e immutabilità degli oggetti. 2,3,4,5 sono immutabili in pitone. Fare riferimento all'immagine seguente. 2 ha un ID fisso fino a questo processo Python.
x ++ significherebbe essenzialmente un incremento sul posto come C. In C, x ++ esegue incrementi sul posto. Quindi, x = 3 e x ++ aumenterebbero 3 in memoria a 4, diversamente da Python dove 3 esisterebbe ancora in memoria.
Pertanto in Python non è necessario ricreare un valore in memoria. Ciò può comportare ottimizzazioni delle prestazioni.
Questa è una risposta basata sul sospetto.
So che questo è un vecchio thread, ma il caso d'uso più comune per ++ i non è coperto, essendo l'indicizzazione manuale imposta quando non ci sono indici forniti. Questa situazione è il motivo per cui python fornisce enumerate ()
Esempio: in una determinata lingua, quando usi un costrutto come foreach per iterare su un set - per il bene dell'esempio diremo anche che è un set non ordinato e hai bisogno di un indice univoco per tutto per distinguerli, per esempio
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
In casi come questo, python fornisce un metodo enumerato, ad es
for i, (key, val) in enumerate(stuff.items()) :