Perché il confronto delle stringhe utilizzando '==' o 'is' a volte produce un risultato diverso?


1147

Ho un programma Python in cui due variabili sono impostate sul valore 'public'. In un'espressione condizionale ho il confronto var1 is var2che fallisce, ma se lo cambio ad var1 == var2esso ritorna True.

Ora, se apro il mio interprete Python e faccio lo stesso confronto "is", ci riesce.

>>> s1 = 'public'
>>> s2 = 'public'
>>> s2 is s1
True

Cosa mi sto perdendo qui?



3
Questo problema si verifica anche quando si legge un ingresso console tramite ad esempio: input = raw_input("Decide (y/n): "). In questo caso un input di "y" e if input == 'y':restituirà "True" mentre if input is 'y':restituirà False.
Semjon Mössinger,

4
Questo blog fornisce una spiegazione molto più completa di qualsiasi risposta guilload.com/python-string-interning
Chris_Rands

1
Come @ chris-rico menziona, ho grande spiegazione qui stackoverflow.com/q/15541404/1695680
ThorSummoner

Risposte:


1533

isè test di identità, ==è test di uguaglianza. cosa succede nel tuo codice verrebbe emulato nell'interprete in questo modo:

>>> a = 'pub'
>>> b = ''.join(['p', 'u', 'b'])
>>> a == b
True
>>> a is b
False

quindi, non c'è da stupirsi che non siano gli stessi, giusto?

In altre parole: isè ilid(a) == id(b)


17
ahh come l'eq? vs uguale? nello schema, capito.
jottos

47
O ==vs .equals()in Java. La parte migliore è che Python ==non è analogo a Java ==.
MatrixFrog

11
@ Крайст: esiste un solo Nonevalore. Quindi ha sempre lo stesso ID.
SilentGhost,

18
Questo non riguarda l'esempio "is -> True" del PO.
user2864740

6
@AlexanderSupertramp, a causa del interning della stringa .
Chris Rico,

570

Altre risposte qui sono corrette: isviene utilizzato per il confronto delle identità , mentre ==viene utilizzato per il confronto delle pari opportunità . Poiché ciò che ti interessa è l'uguaglianza (le due stringhe dovrebbero contenere gli stessi caratteri), in questo caso ilis operatore ha semplicemente torto e dovresti ==invece utilizzare .

Il motivo per cui isfunziona in modo interattivo è che (la maggior parte) letterali di stringhe sono internati per impostazione predefinita. Da Wikipedia:

Le stringhe internate velocizzano i confronti delle stringhe, che a volte rappresentano un collo di bottiglia nelle prestazioni delle applicazioni (come compilatori e runtime del linguaggio di programmazione dinamico) che dipendono fortemente dalle tabelle hash con le chiavi stringa. Senza internare, verificare che due stringhe diverse siano uguali implica esaminare ogni carattere di entrambe le stringhe. Questo è lento per diversi motivi: è intrinsecamente O (n) nella lunghezza delle stringhe; richiede in genere letture da diverse aree della memoria, che richiedono tempo; e le letture riempiono la cache del processore, il che significa che c'è meno cache disponibile per altre esigenze. Con le stringhe internate, è sufficiente un semplice test di identità dell'oggetto dopo l'operazione di intern originale; questo è in genere implementato come test di uguaglianza del puntatore,

Quindi, quando hai due letterali di stringhe (parole che sono letteralmente digitate nel codice sorgente del tuo programma, racchiuse tra virgolette) nel tuo programma che hanno lo stesso valore, il compilatore Python eseguirà automaticamente internamente le stringhe, rendendole entrambe memorizzate nello stesso posizione della memoria. (Nota che questo non sempre succede , e le regole per quando ciò accade sono piuttosto contorte, quindi per favore non fare affidamento su questo comportamento nel codice di produzione!)

Poiché nella sessione interattiva entrambe le stringhe sono effettivamente archiviate nella stessa posizione di memoria, hanno la stessa identità , quindi l' isoperatore funziona come previsto. Ma se costruisci una stringa con qualche altro metodo (anche se quella stringa contiene esattamente gli stessi caratteri), allora la stringa potrebbe essere uguale , ma non è la stessa stringa - cioè, ha un'identità diversa , perché è memorizzato in un posto diverso nella memoria.


6
Dove si può leggere di più sulle regole contorte per quando le stringhe vengono internate?
Noctis Skytower,

89
+1 per una spiegazione approfondita. Non sono sicuro di come l'altra risposta abbia ricevuto così tanti voti senza spiegare cosa sia successo VERAMENTE.
That1 Acquista il

4
questo è esattamente quello che ho pensato quando ho letto la domanda. La risposta accettata è breve ma contiene il fatto, ma questa risposta spiega le cose molto meglio. Bello!
Sнаđошƒаӽ,

3
@NoctisSkytower Googled lo stesso e ho
xtreak

5
@ naught101: No, la regola è scegliere tra ==e in isbase al tipo di controllo desiderato. Se ti preoccupi che le stringhe siano uguali (ovvero che abbiano gli stessi contenuti), dovresti sempre usare ==. Se ti interessa se due nomi Python si riferiscono alla stessa istanza di oggetto, dovresti usare is. Potresti aver bisogno isse stai scrivendo un codice che gestisce molti valori diversi senza preoccuparti del loro contenuto, oppure se sai che c'è solo uno di qualcosa e vuoi ignorare altri oggetti che fingono di essere quella cosa. Se non sei sicuro, scegli sempre ==.
Daniel Pryden,

108

La isparola chiave è un test per l'identità dell'oggetto mentre== è un confronto di valori.

Se si utilizza is, il risultato sarà vero se e solo se l'oggetto è lo stesso oggetto. Tuttavia, ==sarà vero ogni volta che i valori dell'oggetto sono gli stessi.


57

Un'ultima cosa da notare, è possibile utilizzare la sys.internfunzione per assicurarsi di ottenere un riferimento alla stessa stringa:

>>> from sys import intern
>>> a = intern('a')
>>> a2 = intern('a')
>>> a is a2
True

Come indicato sopra, non dovresti usare isper determinare l'uguaglianza delle stringhe. Ma questo può essere utile per sapere se hai qualche tipo di strano requisito da usareis .

Si noti che la internfunzione era incorporata in Python 2 ma è stata spostata nel sysmodulo in Python 3.


43

isè test di identità, ==è test di uguaglianza. Ciò significa che isè un modo per verificare se due cose sono uguali o equivalenti.

Supponi di avere un personoggetto semplice . Se si chiama "Jack" e ha "23", equivale a un altro Jack di 23 anni, ma non è la stessa persona.

class Person(object):
   def __init__(self, name, age):
       self.name = name
       self.age = age

   def __eq__(self, other):
       return self.name == other.name and self.age == other.age

jack1 = Person('Jack', 23)
jack2 = Person('Jack', 23)

jack1 == jack2 #True
jack1 is jack2 #False

Hanno la stessa età, ma non sono la stessa istanza di persona. Una stringa potrebbe essere equivalente a un'altra, ma non è lo stesso oggetto.


Se cambi set jack1.age = 99, questo non cambierà jack2.age. Questo perché sono due casi diversi, quindi jack1 is not jack2. Tuttavia, possono eguagliarsi a vicenda jack1 == jack2se il loro nome e la loro età sono uguali. Diventa più complicato per le stringhe, perché le stringhe sono immutabili in Python e Python riutilizza spesso la stessa istanza. Mi piace questa spiegazione perché usa i casi semplici (un oggetto normale) piuttosto che i casi speciali (stringhe).
Flimm,


28

Se non sei sicuro di ciò che stai facendo, usa "==". Se hai un po 'più di conoscenza a riguardo puoi usare' is 'per oggetti noti come' None '.

Altrimenti ti chiederai perché le cose non funzionano e perché questo accade:

>>> a = 1
>>> b = 1
>>> b is a
True
>>> a = 6000
>>> b = 6000
>>> b is a
False

Non sono nemmeno sicuro che alcune cose rimangano invariate tra le diverse versioni / implementazioni di Python.


1
Esempio interessante che mostra come la riassegnazione degli ints inneschi questa condizione. Perché questo non è riuscito? È dovuto al tirocinio o qualcos'altro?
Paul,

Sembra che il motivo per cui restituisce false potrebbe essere dovuto all'implementazione dell'interprete: stackoverflow.com/questions/132988/…
Paul


@ArchitJain Sì, questi link lo spiegano abbastanza bene. Quando li leggi, saprai su quali numeri puoi usare 'is'. Vorrei solo che spiegassero perché non è ancora una buona idea farlo :) Sapendo che questo non lo rende una buona idea assumere anche tutti gli altri (o che l'intervallo di numeri interiorizzato non cambierà mai)
Mattias Nilsson

20

Dalla mia esperienza limitata con Python, isviene utilizzato per confrontare due oggetti per vedere se sono lo stesso oggetto rispetto a due oggetti diversi con lo stesso valore. ==viene utilizzato per determinare se i valori sono identici.

Ecco un buon esempio:

>>> s1 = u'public'
>>> s2 = 'public'
>>> s1 is s2
False
>>> s1 == s2
True

s1è una stringa unicode ed s2è una stringa normale. Non sono dello stesso tipo, ma hanno lo stesso valore.


17

Penso che abbia a che fare con il fatto che, quando il confronto 'is' viene valutato falso, vengono utilizzati due oggetti distinti. Se si valuta vero, significa che internamente utilizza lo stesso oggetto esatto e non ne crea uno nuovo, probabilmente perché li hai creati in una frazione di circa 2 secondi e perché non c'è un grande intervallo di tempo tra l'ottimizzazione e usa lo stesso oggetto.

Questo è il motivo per cui dovresti usare l'operatore di uguaglianza ==, non is, per confrontare il valore di un oggetto stringa.

>>> s = 'one'
>>> s2 = 'two'
>>> s is s2
False
>>> s2 = s2.replace('two', 'one')
>>> s2
'one'
>>> s2 is s
False
>>> 

In questo esempio, ho creato s2, che era un oggetto stringa diverso in precedenza uguale a "uno" ma non è lo stesso oggetto di s, poiché l'interprete non ha utilizzato lo stesso oggetto poiché inizialmente non l'ho assegnato a "uno", se l'avessi, li avrebbe resi lo stesso oggetto.


3
L'uso .replace()come esempio in questo contesto non è probabilmente il migliore, tuttavia, poiché la sua semantica può essere fonte di confusione. s2 = s2.replace()creerà sempre un nuovo oggetto stringa, assegnerà il nuovo oggetto stringa s2e quindi eliminerà l'oggetto stringa s2a cui puntava. Quindi, anche se lo facessi, s = s.replace('one', 'one')otterrai comunque un nuovo oggetto stringa.
Daniel Pryden,

13

Credo che questo sia noto come stringhe "internate". Python lo fa, così come Java, così come C e C ++ durante la compilazione in modalità ottimizzata.

Se si utilizzano due stringhe identiche, invece di sprecare memoria creando due oggetti stringa, tutte le stringhe internate con lo stesso contenuto puntano alla stessa memoria.

Ciò comporta che l'operatore "is" di Python restituisce True perché due stringhe con lo stesso contenuto puntano allo stesso oggetto stringa. Questo accadrà anche in Java e in C.

Questo è utile solo per risparmiare memoria. Non puoi fare affidamento su di esso per verificare l'uguaglianza delle stringhe, perché i vari interpreti e compilatori e motori JIT non possono sempre farlo.


12

Sto rispondendo alla domanda anche se la domanda è troppo vecchia perché nessuna risposta sopra cita il riferimento linguistico

In realtà è l'operatore controlla l'identità e == l'operatore verifica l'uguaglianza,

Dal riferimento linguistico:

I tipi influenzano quasi tutti gli aspetti del comportamento degli oggetti. Anche l'importanza dell'identità dell'oggetto è influenzata in qualche modo: per i tipi immutabili, le operazioni che calcolano nuovi valori possono effettivamente restituire un riferimento a qualsiasi oggetto esistente con lo stesso tipo e valore, mentre per gli oggetti mutabili ciò non è consentito . Ad esempio, dopo a = 1; b = 1, a e b possono o meno riferirsi allo stesso oggetto con il valore uno, a seconda dell'implementazione, ma dopo c = []; d = [], c e d sono garantiti per fare riferimento a due elenchi vuoti diversi, univoci e di nuova creazione. (Notare che c = d = [] assegna lo stesso oggetto sia a c che a d.)

quindi dall'istruzione precedente possiamo dedurre che le stringhe che sono di tipo immutabile potrebbero non funzionare correttamente quando viene verificato con "is" e potrebbero avere esito positivo se verificate con "is"

Lo stesso vale per int, tuple che sono anche tipi immutabili


8

L' ==equivalenza del valore di prova dell'operatore. L' isoperatore verifica l'identità dell'oggetto, Python verifica se i due sono realmente lo stesso oggetto (ovvero vivono allo stesso indirizzo in memoria).

>>> a = 'banana'
>>> b = 'banana'
>>> a is b 
True

In questo esempio, Python ha creato solo un oggetto stringa, ed entrambi ae bfa riferimento ad esso. Il motivo è che Python memorizza internamente nella cache e riutilizza alcune stringhe come ottimizzazione, in realtà c'è solo una stringa "banana" in memoria, condivisa da aeb; Per attivare il comportamento normale, è necessario utilizzare stringhe più lunghe:

>>> a = 'a longer banana'
>>> b = 'a longer banana'
>>> a == b, a is b
(True, False)

Quando si creano due elenchi, si ottengono due oggetti:

>>> a = [1, 2, 3]
>>> b = [1, 2, 3]
>>> a is b
False

In questo caso diremmo che le due liste sono equivalenti, perché hanno gli stessi elementi, ma non identici, perché non sono lo stesso oggetto. Se due oggetti sono identici, sono anche equivalenti, ma se sono equivalenti, non sono necessariamente identici.

Se si ariferisce a un oggetto e si assegna b = a, quindi entrambe le variabili si riferiscono allo stesso oggetto:

>>> a = [1, 2, 3]
>>> b = a
>>> b is a
True

7

isconfronterà la posizione della memoria. Viene utilizzato per il confronto a livello di oggetto.

==confronterà le variabili nel programma. Viene utilizzato per il controllo a livello di valore.

is verifica l'equivalenza a livello di indirizzo

== verifica l'equivalenza del livello di valore


3

isè un test di identità, ==è un test di uguaglianza (vedi la documentazione di Python ).

Nella maggior parte dei casi, se a is b, allora a == b. Ma ci sono eccezioni, ad esempio:

>>> nan = float('nan')
>>> nan is nan
True
>>> nan == nan
False

Quindi, puoi utilizzare solo isper test di identità, mai test di uguaglianza.

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.