Perché non ci sono operatori ++ e - in Python?


Risposte:


443

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.


98
È spesso utile usare qualcosa come array [i ++], che non è ordinato con + = / - =.
Turner Hayes,

102
@thayes: Questo non è un modello comune in Python.
Glenn Maynard,

9
@thayes Dato che sarà all'interno di un ciclo, potresti anche collegarti idirettamente - se ne hai davvero bisogno e non puoi semplicemente usarloarray.append()
Tobias Kienzler

31
Vedo che la preoccupazione molto più grande è la leggibilità e la prevedibilità. Nei miei giorni in C, ho visto più che sufficienti bug derivanti da incomprensioni sulla distinzione tra i++e ++i...
Charles Duffy,

5
In aggiunta alla giustificazione del dopo-fatto: su un progetto su cui lavoro, ho riscontrato (più di chiunque altro nella loro vita) una discreta quantità di codice C ++ che soffriva di problemi ++e --veniva usato in modi che risultano indefiniti o non specificati comportamento. Consentono di scrivere codice complicato, difficile da analizzare correttamente.
Brian Vandenberg,

84

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 *++pe *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.


4
Javascript ha ++. Non credo sia un "trucco per comunicare le intenzioni all'assemblatore". Inoltre, Python ha bytecode. Quindi penso che il motivo sia qualcos'altro.
Nathan Davis,

13
Questo "fornire suggerimenti al compilatore" è davvero un mito. Francamente, è un'aggiunta stupida a qualsiasi lingua e viola i seguenti due precetti: 1. Non codifichi che il computer legga, codifichi che un altro ingegnere legga. E 2. Non codifichi che un ingegnere competente legga, codifichi che un ingegnere competente legga mentre è esaurito alle 3 del mattino e si è fatto il pieno di caffeina.

3
@ tgm1024 Per essere onesti, quando si digita un teletipo half duplex di 10-30 caratteri al secondo, si codifica in modo da poterlo digitare prima della prossima settimana.
msw,

4
@ tgm1024 Unix e C hanno visto la maggior parte del loro sviluppo iniziale sui PDP-11 che utilizzavano tipi di telefono incredibilmente lenti per la comunicazione dell'utente. Mentre hai perfettamente ragione che oggi la codifica per la macchina è per lo più sciocca, allora era l'interfaccia Human / Machine che era il collo di bottiglia. È difficile immaginare di lavorare così lentamente se non dovessi mai farlo.
msw,

65

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.


10
one--è zero?
Andre Holzner,

25
one--è uno nella frase, ma zero immediatamente dopo. Quindi questo 'koan' suggerisce anche che gli operatori di incremento / decremento non sono ovvi.
Victor K

14
@EralpB Se elimini + =, non puoi fare cose come x + = 10. + = è un caso più generale di ++
Rory

8
Inoltre: "Esplicito è meglio che implicito".
Ber,

2
Sicuramente non è lo stesso, perché x + = 1 NON è un'espressione - è un'affermazione - e non valuta nulla. Non puoi fare cose come: 'row [col ++] = a; riga [col ++] = b '. Per non parlare delle cose pre-inc e post-inc di c ++.
Uri,

38

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:

  • Mescola insieme espressioni ed espressioni, che non è una buona pratica. Vedi http://norvig.com/python-iaq.html
  • In genere incoraggia le persone a scrivere codice meno leggibile
  • Ulteriore complessità nell'implementazione del linguaggio, che non è necessaria in Python, come già accennato

12
Sono contento che qualcuno abbia finalmente menzionato l'aspetto dell'affermazione contro l'espressione. In C l'assegnazione è un'espressione e quindi l'operatore ++. In Python l'assegnazione è un'istruzione, quindi se avesse un ++, probabilmente dovrebbe essere anche un'istruzione di assegnazione (e ancor meno utile o necessaria).
martineau,

D'accordo - se fossero dichiarazioni, almeno sarebbe assolutamente insignificante parlare della differenza tra post e pre-operatori.
Crowman,

17

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.


gli ints sono immutabili anche in C. Se non la pensi così, prova a fare in modo che il tuo compilatore C generi codice per 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!
Lutz Prechelt,

10
Giusto. 42 è una costante letterale . Le costanti sono (o almeno dovrebbero essere) immutabili. Ciò non significa che i C intin generale siano immutabili. Un intin C indica semplicemente un posto nella memoria. E i bit in quel posto sono molto mutabili. Ad esempio, è possibile creare un riferimento di an inte modificare il riferimento di quel riferimento. Questa modifica è visibile in tutti i riferimenti (inclusa la intvariabile originale ) in quel luogo. Lo stesso non vale per un oggetto intero Python.
Nathan Davis,

x + = 1 non finisce per diventare inc mem. Python esegue una chiamata di funzione per aggiungere 1 a una variabile; è patetico.
PalaDolphin,

12

Chiarezza!

Python è molto incentrato sulla chiarezza e nessun programmatore è in grado di indovinare correttamente il significato a --ameno 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).


10

La mia comprensione del motivo per cui python non ha un ++operatore è la seguente: quando scrivi questo in python a=b=c=1otterrai 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

aOra puoi vedere che la variabile punta a un altro oggetto come variabili be 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 anuovo 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:

In Python, perché una funzione può modificare alcuni argomenti come percepiti dal chiamante, ma non altri?

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

Le funzioni Python chiamano per riferimento

Code Like a Pythonista: Idiomatic Python


9

È 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.


3
Sì amico, potresti sostituirlo return a[i++]con return a[i=i+1].
Luís de Sousa,

7

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.


In questo aspetto, x++è più vicino x += 1che a x = x + 1, questi due fanno la differenza anche su oggetti mutabili.
glglgl,

4

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 '.


3

Credo che derivi dal credo di Python che "esplicito è meglio che implicito".


Bene, non scrivi esplicitamente le istruzioni "inizio" e "fine" in Python, giusto? Anche se sono d'accordo con l'affermazione, penso che ci siano dei limiti. Mentre possiamo discutere su questi confini, penso che tutti possiamo concordare sul fatto che esiste una linea, che non è pratico da attraversare. E poiché ci sono così tante opinioni e giustificazioni su quella decisione, non penso che sia stata una scelta chiara. Almeno, non riesco a trovare una fonte, dove è esplicitamente dichiarato
Hasan Ammori,

3

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.


2

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 ++.


1
nulla impedisce di chiamare una funzione con un effetto collaterale in una comprensione test / espressione / lista: f(a)dov'è auna lista, qualche oggetto immutabile.
Jean-François Fabre

1

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.


1

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.


1
Quindi, cosa rende questo ++e diverso da += 1?
Ber,

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.


1

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.


2
: = assegnazione è una vergogna
nehem

0

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.

ID di costanti e variabili

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.


0

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()) :
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.