L'operatore "is" si comporta inaspettatamente con i numeri interi


509

Perché in Python si comportano in modo imprevisto?

>>> a = 256
>>> b = 256
>>> a is b
True           # This is an expected result
>>> a = 257
>>> b = 257
>>> a is b
False          # What happened here? Why is this False?
>>> 257 is 257
True           # Yet the literal numbers compare properly

Sto usando Python 2.5.2. Provando alcune versioni diverse di Python, sembra che Python 2.3.3 mostri il comportamento sopra riportato tra 99 e 100.

Sulla base di quanto sopra, posso ipotizzare che Python sia implementato internamente in modo tale che numeri interi "piccoli" siano archiviati in modo diverso rispetto ai numeri interi più grandi e l' isoperatore possa dire la differenza. Perché l'astrazione che perde? Qual è un modo migliore di confrontare due oggetti arbitrari per vedere se sono uguali quando non so in anticipo se sono numeri o no?


1
Dai un'occhiata qui > L'implementazione corrente mantiene un array di oggetti interi per tutti> numeri interi compresi tra -5 e 256, quando crei un int in quell'intervallo> in realtà ottieni semplicemente un riferimento all'oggetto esistente.
user5319825,

2
Questo è un dettaglio dell'implementazione specifico di CPython e un comportamento indefinito, usare con cautela
ospider

Risposte:


393

Guarda questo:

>>> a = 256
>>> b = 256
>>> id(a)
9987148
>>> id(b)
9987148
>>> a = 257
>>> b = 257
>>> id(a)
11662816
>>> id(b)
11662828

Ecco cosa ho trovato nella documentazione di Python 2, "Plain Integer Objects" (È lo stesso per Python 3 ):

L'implementazione corrente mantiene un array di oggetti interi per tutti i numeri interi compresi tra -5 e 256, quando si crea un int in quell'intervallo in realtà si ottiene semplicemente un riferimento all'oggetto esistente. Quindi dovrebbe essere possibile modificare il valore di 1. Sospetto che il comportamento di Python in questo caso non sia definito. :-)


46
qualcuno sa come è stato scelto quell'intervallo (-5, 256)? non sarei troppo sorpreso se fosse (0, 255) o anche (-255, 255), ma un intervallo di 262 numeri che inizia a -5 sembra sorprendentemente arbitrario.
Woodrow Barlow,

6
@WoodrowBarlow: Il -5 è solo un'euristica per catturare segnaposto negativi comuni, credo. 0..255 copre le matrici di valori a byte singolo. 256 è misterioso, ma suppongo sia per (dis) assemblare numeri interi in / da byte.
Davis Herring,

3
Da quello che ho capito la gamma è stata scelta osservando i valori comunemente usati in più progetti (e più lingue).
Tony Suffolk, 66

9
Secondo reddit.com/r/Python/comments/18leav/… , l'intervallo era di [-5,100]. È stato espanso per includere l'intera gamma di valori byte - più 256, perché presumibilmente è un numero comune.
mwfearnley,

2
@Ashwani prova a leggere i commenti accanto al tuo commento, pubblicati due anni prima dei tuoi, e troverai la risposta alla tua domanda.
jbg

116

L'operatore "is" di Python si comporta inaspettatamente con gli interi?

In sintesi, vorrei sottolineare: non utilizzare isper confrontare numeri interi.

Questo non è un comportamento che dovresti avere delle aspettative.

Invece, usa ==e !=per confrontare rispettivamente per uguaglianza e disuguaglianza. Per esempio:

>>> a = 1000
>>> a == 1000       # Test integers like this,
True
>>> a != 5000       # or this!
True
>>> a is 1000       # Don't do this! - Don't use `is` to test integers!!
False

Spiegazione

Per saperlo, devi sapere quanto segue.

Innanzitutto cosa fa is? È un operatore di confronto. Dalla documentazione :

Operatori ise is nottest per l'identità dell'oggetto: x is yè vero se e solo se xey sono lo stesso oggetto. x is not yproduce il valore della verità inversa.

E quindi i seguenti sono equivalenti.

>>> a is b
>>> id(a) == id(b)

Dalla documentazione :

id Restituisce l '"identità" di un oggetto. Questo è un numero intero (o intero lungo) che è garantito essere unico e costante per questo oggetto durante la sua vita. Due oggetti con durata non sovrapposta possono avere lo stesso id()valore.

Si noti che il fatto che l'id di un oggetto in CPython (l'implementazione di riferimento di Python) sia la posizione in memoria è un dettaglio di implementazione. Altre implementazioni di Python (come Jython o IronPython) potrebbero facilmente avere un'implementazione diversa per id.

Allora, a che serve la pratica is? PEP8 descrive :

I confronti con i singleton come Nonedovrebbero sempre essere fatti con iso is not, mai, gli operatori di uguaglianza.

La domanda

Poni e dichiari la seguente domanda (con codice):

Perché in Python si comportano in modo imprevisto?

>>> a = 256
>>> b = 256
>>> a is b
True           # This is an expected result

E ' non è un risultato atteso. Perché è previsto? Significa solo che gli interi valutati come 256referenziati da entrambi aeb sono la stessa istanza di numero intero. I numeri interi sono immutabili in Python, quindi non possono cambiare. Ciò non dovrebbe avere alcun impatto su alcun codice. Non dovrebbe essere previsto. È solo un dettaglio di implementazione.

Ma forse dovremmo essere contenti che non ci sia una nuova istanza separata in memoria ogni volta che affermiamo che un valore è uguale a 256.

>>> a = 257
>>> b = 257
>>> a is b
False          # What happened here? Why is this False?

Sembra che ora abbiamo due istanze separate di numeri interi con il valore di 257in memory. Poiché i numeri interi sono immutabili, questo spreca memoria. Speriamo di non sprecare molto. Probabilmente no. Ma questo comportamento non è garantito.

>>> 257 is 257
True           # Yet the literal numbers compare properly

Bene, sembra che la tua particolare implementazione di Python stia cercando di essere intelligente e di non creare numeri interi ridondanti in memoria, a meno che non sia necessario. Sembra che tu stia utilizzando l'implementazione referente di Python, che è CPython. Buono per CPython.

Potrebbe essere ancora meglio se CPython potesse farlo a livello globale, se potesse farlo in modo economico (come ci sarebbe un costo nella ricerca), forse un'altra implementazione potrebbe.

Ma per quanto riguarda l'impatto sul codice, non dovresti preoccuparti se un numero intero è un'istanza particolare di un numero intero. Dovresti preoccuparti solo del valore di quell'istanza, e per questo useresti i normali operatori di confronto, ad es ==.

Cosa isfa

iscontrolla che i iddue oggetti siano uguali. In CPython, idè la posizione in memoria, ma potrebbe essere un altro numero identificativo univoco in un'altra implementazione. Per ribadire questo con il codice:

>>> a is b

equivale a

>>> id(a) == id(b)

Perché dovremmo usare isallora?

Questo può essere un controllo molto veloce da dire, verificando se due stringhe molto lunghe hanno lo stesso valore. Ma poiché si applica all'unicità dell'oggetto, abbiamo quindi dei casi d'uso limitati per esso. In effetti, vogliamo principalmente usarlo per verificare None, che è un singleton (una sola istanza esistente in un posto in memoria). Potremmo creare altri singleton se esiste il potenziale per confonderli, che potremmo verificare is, ma questi sono relativamente rari. Ecco un esempio (funzionerà in Python 2 e 3) ad es

SENTINEL_SINGLETON = object() # this will only be created one time.

def foo(keyword_argument=None):
    if keyword_argument is None:
        print('no argument given to foo')
    bar()
    bar(keyword_argument)
    bar('baz')

def bar(keyword_argument=SENTINEL_SINGLETON):
    # SENTINEL_SINGLETON tells us if we were not passed anything
    # as None is a legitimate potential argument we could get.
    if keyword_argument is SENTINEL_SINGLETON:
        print('no argument given to bar')
    else:
        print('argument to bar: {0}'.format(keyword_argument))

foo()

Che stampa:

no argument given to foo
no argument given to bar
argument to bar: None
argument to bar: baz

E così vediamo, con ise una sentinella, siamo in grado di distinguere tra quando barviene chiamato senza argomenti e quando viene chiamato con None. Questi sono i principali casi d'uso per is- non usarlo per verificare l'uguaglianza di numeri interi, stringhe, tuple o altre cose come queste.


"Questi sono i principali casi d'uso per is- non usarli per verificare l'uguaglianza di numeri interi, stringhe, tuple o altre cose come queste." Tuttavia, sto cercando di integrare una semplice macchina a stati nella mia classe, e poiché gli stati sono valori opachi la cui unica proprietà osservabile è quella di essere identici o diversi, sembra abbastanza naturale che possano essere comparabili is. Ho intenzione di usare stringhe internate come stati. Avrei preferito interi semplici, ma sfortunatamente Python non può internare interi ( 0 is 0è un dettaglio di implementazione).
Alexey,

@Alexey sembra che tu abbia bisogno di enumerazioni? stackoverflow.com/questions/37601644/…
Aaron Hall

Forse, grazie, non li conoscevo. Questa potrebbe essere un'aggiunta appropriata alla tua risposta IMO.
Alexey,

Forse usare un numero di oggetti stupidi come la sentinella nella tua risposta sarebbe una soluzione più leggera ...
Alexey,

Gli enumerati @Alexey si trovano nella libreria standard di Python 3 e ciò probabilmente incoraggerebbe il tuo codice ad essere un po 'più significativo rispetto alle sentinelle nude.
Aaron Hall

60

Dipende se stai cercando di vedere se 2 cose sono uguali o lo stesso oggetto.

iscontrolla se sono lo stesso oggetto, non solo uguale. I piccoli ints stanno probabilmente puntando alla stessa posizione di memoria per l'efficienza dello spazio

In [29]: a = 3
In [30]: b = 3
In [31]: id(a)
Out[31]: 500729144
In [32]: id(b)
Out[32]: 500729144

Dovresti usare ==per confrontare l'uguaglianza di oggetti arbitrari. È possibile specificare il comportamento con gli attributi __eq__e __ne__.


Complimenti per aver effettivamente spiegato come confrontare oggetti arbitrari, come l'OP ha chiesto !!
Joooeey

54

Sono in ritardo ma, vuoi una fonte con la tua risposta? Cercherò di esprimerlo in modo introduttivo in modo che più persone possano seguirlo.


Un aspetto positivo di CPython è che puoi effettivamente vedere la fonte per questo. Userò i collegamenti per la versione 3.5 , ma trovo il corrispondente 2.x è banale.

In CPython, il C-API che gestisce la creazione di un nuovo intoggetto è PyLong_FromLong(long v). La descrizione per questa funzione è:

L'implementazione corrente mantiene un array di oggetti interi per tutti i numeri interi compresi tra -5 e 256, quando si crea un int in quell'intervallo in realtà si ottiene semplicemente un riferimento all'oggetto esistente . Quindi dovrebbe essere possibile modificare il valore di 1. Sospetto che il comportamento di Python in questo caso non sia definito. :-)

(Il mio corsivo)

Non ti conosco ma lo vedo e penso: troviamo quell'array!

Se non hai armeggiato con il codice C che implementa CPython dovresti ; tutto è abbastanza organizzato e leggibile. Nel nostro caso, dobbiamo guardare nella Objectssottodirectory dell'albero principale del codice sorgente .

PyLong_FromLongtratta gli longoggetti, quindi non dovrebbe essere difficile dedurre che dobbiamo sbirciare dentro longobject.c. Dopo aver guardato dentro potresti pensare che le cose siano caotiche; sono, ma non temono, la funzione che stiamo cercando è agghiacciante alla linea 230 che ci aspetta per verificarlo. È una funzione piccola, quindi il corpo principale (escluse le dichiarazioni) è facilmente incollato qui:

PyObject *
PyLong_FromLong(long ival)
{
    // omitting declarations

    CHECK_SMALL_INT(ival);

    if (ival < 0) {
        /* negate: cant write this as abs_ival = -ival since that
           invokes undefined behaviour when ival is LONG_MIN */
        abs_ival = 0U-(unsigned long)ival;
        sign = -1;
    }
    else {
        abs_ival = (unsigned long)ival;
    }

    /* Fast path for single-digit ints */
    if (!(abs_ival >> PyLong_SHIFT)) {
        v = _PyLong_New(1);
        if (v) {
            Py_SIZE(v) = sign;
            v->ob_digit[0] = Py_SAFE_DOWNCAST(
                abs_ival, unsigned long, digit);
        }
        return (PyObject*)v; 
}

Ora, non siamo C master-code-haxxorz ma non siamo nemmeno stupidi, possiamo vederlo CHECK_SMALL_INT(ival);sbirciare tutti in modo seducente; possiamo capire che ha qualcosa a che fare con questo. Controlliamolo:

#define CHECK_SMALL_INT(ival) \
    do if (-NSMALLNEGINTS <= ival && ival < NSMALLPOSINTS) { \
        return get_small_int((sdigit)ival); \
    } while(0)

Quindi è una macro che chiama la funzione get_small_intse il valore ivalsoddisfa la condizione:

if (-NSMALLNEGINTS <= ival && ival < NSMALLPOSINTS)

Quindi cosa sono NSMALLNEGINTSe NSMALLPOSINTS? Macro! Eccoli :

#ifndef NSMALLPOSINTS
#define NSMALLPOSINTS           257
#endif
#ifndef NSMALLNEGINTS
#define NSMALLNEGINTS           5
#endif

Quindi la nostra condizione è if (-5 <= ival && ival < 257) chiamata get_small_int.

Ora diamo un'occhiata a get_small_inttutta la sua gloria (beh, vedremo solo il suo corpo perché è lì che sono le cose interessanti):

PyObject *v;
assert(-NSMALLNEGINTS <= ival && ival < NSMALLPOSINTS);
v = (PyObject *)&small_ints[ival + NSMALLNEGINTS];
Py_INCREF(v);

Va bene, dichiarare a PyObject, affermare che la condizione precedente è valida ed eseguire l'incarico:

v = (PyObject *)&small_ints[ival + NSMALLNEGINTS];

small_intsassomiglia molto all'array che stavamo cercando, ed è così! Avremmo potuto leggere la dannata documentazione e lo avremmo saputo da sempre! :

/* Small integers are preallocated in this array so that they
   can be shared.
   The integers that are preallocated are those in the range
   -NSMALLNEGINTS (inclusive) to NSMALLPOSINTS (not inclusive).
*/
static PyLongObject small_ints[NSMALLNEGINTS + NSMALLPOSINTS];

Quindi sì, questo è il nostro ragazzo. Quando vuoi crearne uno nuovo intnell'intervallo[NSMALLNEGINTS, NSMALLPOSINTS) si ottiene semplicemente un riferimento a un oggetto già esistente che è stato preallocato.

Poiché il riferimento si riferisce allo stesso oggetto, emette id()direttamente o verifica l'identità conis su restituirà esattamente la stessa cosa.

Ma quando vengono assegnati ??

Durante l'inizializzazione in_PyLong_Init Python entrerò volentieri in un ciclo for, fallo per te:

for (ival = -NSMALLNEGINTS; ival <  NSMALLPOSINTS; ival++, v++) {

Controlla la fonte per leggere il corpo del loop!

Spero che la mia spiegazione ti abbia reso C cose chiaramente ora (gioco di parole ovviamente inteso).


Ma 257 is 257? Che cosa succede?

Questo è in realtà più facile da spiegare, e ho già provato a farlo ; è dovuto al fatto che Python eseguirà questa istruzione interattiva come un singolo blocco:

>>> 257 is 257

Durante la compilazione di questa affermazione, CPython vedrà che hai due letterali corrispondenti e userà la stessa PyLongObjectrappresentazione 257. Puoi vederlo se esegui la compilazione da solo e ne esamini il contenuto:

>>> codeObj = compile("257 is 257", "blah!", "exec")
>>> codeObj.co_consts
(257, None)

Quando CPython esegue l'operazione, ora caricherà esattamente lo stesso oggetto:

>>> import dis
>>> dis.dis(codeObj)
  1           0 LOAD_CONST               0 (257)   # dis
              3 LOAD_CONST               0 (257)   # dis again
              6 COMPARE_OP               8 (is)

Quindi istornerà True.


37

Come puoi controllare nel file sorgente intobject.c , Python memorizza nella cache piccoli numeri interi per l'efficienza. Ogni volta che crei un riferimento a un numero intero piccolo, fai riferimento al numero intero piccolo memorizzato nella cache, non a un nuovo oggetto. 257 non è un numero intero piccolo, quindi viene calcolato come un oggetto diverso.

È meglio usare ==a tale scopo.


19

Penso che le tue ipotesi siano corrette. Sperimenta con id(identità dell'oggetto):

In [1]: id(255)
Out[1]: 146349024

In [2]: id(255)
Out[2]: 146349024

In [3]: id(257)
Out[3]: 146802752

In [4]: id(257)
Out[4]: 148993740

In [5]: a=255

In [6]: b=255

In [7]: c=257

In [8]: d=257

In [9]: id(a), id(b), id(c), id(d)
Out[9]: (146349024, 146349024, 146783024, 146804020)

Sembra che i numeri <= 255siano trattati come letterali e tutto quanto sopra sia trattato in modo diverso!


1
È perché gli oggetti che rappresentano valori da -5 a +256 vengono creati al momento dell'avvio - e quindi tutti gli usi di tali valori vengono utilizzati per l'oggetto precompilato. Quasi tutti i riferimenti a numeri interi al di fuori di tale intervallo creano un nuovo oggetto interno ogni volta che vengono referenziati. Penso che l'uso del termine letterale sia confuso - letterale si riferisce normalmente a qualsiasi valore digitato in un pezzo di codice - quindi tutti i numeri nel codice sorgente sono letterali.
Tony Suffolk, 66

13

Per oggetti valore immutabili, come ints, stringhe o datetimes, l'identità dell'oggetto non è particolarmente utile. È meglio pensare all'uguaglianza. L'identità è essenzialmente un dettaglio di implementazione per gli oggetti valore - poiché sono immutabili, non c'è alcuna differenza effettiva tra avere più riferimenti allo stesso oggetto o più oggetti.


12

C'è un altro problema che non è indicato in nessuna delle risposte esistenti. Python è autorizzato a unire due valori immutabili e i valori int piccoli predefiniti non sono l'unico modo in cui ciò può accadere. Un'implementazione di Python non è mai garantita per farlo, ma lo fanno tutti per qualcosa di più di piccoli ints.


Per prima cosa, ci sono altri valori pre-creati, come il vuoto tuple, stre bytes, e alcune stringhe brevi (in CPython 3.6, sono le 256 stringhe latine-1 a carattere singolo). Per esempio:

>>> a = ()
>>> b = ()
>>> a is b
True

Ma anche i valori non pre-creati possono essere identici. Considera questi esempi:

>>> c = 257
>>> d = 257
>>> c is d
False
>>> e, f = 258, 258
>>> e is f
True

E questo non si limita ai intvalori:

>>> g, h = 42.23e100, 42.23e100
>>> g is h
True

Ovviamente, CPython non ha un floatvalore pre-creato per 42.23e100. Allora, cosa sta succedendo qui?

Il compilatore CPython fonderà valori costanti di alcuni tipi noti come immutabili int, float, str, bytes, nella stessa unità di compilazione. Per un modulo, l'intero modulo è un'unità di compilazione, ma all'interprete interattivo ogni istruzione è un'unità di compilazione separata. Poiché ce dsono definiti in istruzioni separate, i loro valori non vengono uniti. Poiché ee fsono definiti nella stessa istruzione, i loro valori vengono uniti.


Puoi vedere cosa succede smontando il bytecode. Prova a definire una funzione che fa e, f = 128, 128e poi a chiamarla dis.dis, e vedrai che c'è un singolo valore costante(128, 128)

>>> def f(): i, j = 258, 258
>>> dis.dis(f)
  1           0 LOAD_CONST               2 ((128, 128))
              2 UNPACK_SEQUENCE          2
              4 STORE_FAST               0 (i)
              6 STORE_FAST               1 (j)
              8 LOAD_CONST               0 (None)
             10 RETURN_VALUE
>>> f.__code__.co_consts
(None, 128, (128, 128))
>>> id(f.__code__.co_consts[1], f.__code__.co_consts[2][0], f.__code__.co_consts[2][1])
4305296480, 4305296480, 4305296480

Potresti notare che il compilatore è stato archiviato 128come costante anche se non è effettivamente utilizzato dal bytecode, il che ti dà un'idea di quanto poca ottimizzazione compila il CPython. Ciò significa che le tuple (non vuote) in realtà non finiscono per unirsi:

>>> k, l = (1, 2), (1, 2)
>>> k is l
False

Mettilo in una funzione, dis e osserva le co_consts—c'è una 1e una 2, due (1, 2)tuple che condividono la stessa 1e 2ma non sono identiche, e una ((1, 2), (1, 2))tupla che ha le due distinte tuple uguali.


C'è un'altra ottimizzazione che fa CPython: interning di stringhe. A differenza della piegatura costante del compilatore, questo non è limitato ai valori letterali del codice sorgente:

>>> m = 'abc'
>>> n = 'abc'
>>> m is n
True

D'altra parte, è limitato al strtipo e alle stringhe del tipo di archiviazione interna "ascii compact", "compact" o "legacy ready" e in molti casi solo "ascii compact" verrà internato.


In ogni caso, le regole per quali valori devono essere, potrebbero essere o non essere distinti variano da implementazione a implementazione e tra versioni della stessa implementazione e forse anche tra esecuzioni dello stesso codice sulla stessa copia della stessa implementazione .

Vale la pena imparare le regole per un Python specifico per il gusto di farlo. Ma non vale la pena affidarsi a loro nel tuo codice. L'unica regola sicura è:

  • Non scrivere codice che assume due valori immutabili uguali ma creati separatamente sono identici (non usare x is y, usarex == y )
  • Non scrivere codice che assume due valori immutabili uguali ma creati separatamente sono distinti (non usare x is not y , usare x != y)

O, in altre parole, utilizzare solo isper testare i singleton documentati (come None) o che vengono creati solo in un punto del codice (come il _sentinel = object()linguaggio).


Il consiglio meno criptico è semplicemente: non usare x is yper confrontare, usare x == y. Allo stesso modo non usare x is not y, usarex != y
smci il

Quindi, guardando a questa domanda , perché è a=257; b=257su una sola riga a is bTrue
Joe

8

is è l'operatore di uguaglianza di identità (che funziona come id(a) == id(b)); è solo che due numeri uguali non sono necessariamente lo stesso oggetto. Per motivi di prestazioni, alcuni piccoli numeri interi vengono memorizzati quindi tenderanno ad essere gli stessi (ciò può essere fatto poiché sono immutabili).

L' === operatore di PHP , d'altra parte, è descritto come il controllo dell'uguaglianza e del tipo: x == y and type(x) == type(y)secondo il commento di Paulo Freitas. Questo sarà sufficiente per i numeri comuni, ma differisce da isper le classi che definiscono __eq__in modo assurdo:

class Unequal:
    def __eq__(self, other):
        return False

PHP apparentemente consente la stessa cosa per le classi "incorporate" (che presumo significhi implementate a livello C, non in PHP). Un uso leggermente meno assurdo potrebbe essere un oggetto timer, che ha un valore diverso ogni volta che viene usato come numero. Non so perché vorresti emulare Visual Basic Nowinvece di dimostrare che si tratta di una valutazione time.time()che non conosco.

Greg Hewgill (OP) ha fatto un commento chiarente "Il mio obiettivo è confrontare l'identità dell'oggetto, piuttosto che l'uguaglianza di valore. Tranne i numeri, dove voglio trattare l'identità dell'oggetto allo stesso modo dell'uguaglianza di valore."

Ciò avrebbe un'altra risposta, dato che dobbiamo classificare le cose come numeri o no, per scegliere se confrontarci con ==o is. CPython definisce il protocollo numerico , incluso PyNumber_Check, ma questo non è accessibile da Python stesso.

Potremmo provare a utilizzare isinstancetutti i tipi di numero che conosciamo, ma ciò sarebbe inevitabilmente incompleto. Il modulo types contiene un elenco StringTypes ma nessun NumberTypes. A partire da Python 2.6, le classi numeriche incorporate hanno una classe base numbers.Number, ma presenta lo stesso problema:

import numpy, numbers
assert not issubclass(numpy.int16,numbers.Number)
assert issubclass(int,numbers.Number)

A proposito, NumPy produrrà istanze separate di numeri bassi.

In realtà non conosco una risposta a questa variante della domanda. Suppongo che teoricamente si possano usare i ctypes per chiamare PyNumber_Check, ma anche quella funzione è stata discussa , e certamente non è portatile. Dovremo solo essere meno particolari su ciò che testiamo per ora.

Alla fine, questo problema deriva dal fatto che Python non aveva originariamente un albero dei tipi con predicati come Scheme number? o la classe di tipi Num di Haskell . iscontrolla l'identità dell'oggetto, non l'uguaglianza di valore. PHP ha anche una storia colorata, dove ===apparentemente si comporta come issolo sugli oggetti in PHP5, ma non in PHP4 . Tali sono i crescenti problemi di spostarsi tra le lingue (comprese le versioni di una).


4

Succede anche con le stringhe:

>>> s = b = 'somestr'
>>> s == b, s is b, id(s), id(b)
(True, True, 4555519392, 4555519392)

Ora sembra tutto a posto.

>>> s = 'somestr'
>>> b = 'somestr'
>>> s == b, s is b, id(s), id(b)
(True, True, 4555519392, 4555519392)

Anche questo è previsto.

>>> s1 = b1 = 'somestrdaasd ad ad asd as dasddsg,dlfg ,;dflg, dfg a'
>>> s1 == b1, s1 is b1, id(s1), id(b1)
(True, True, 4555308080, 4555308080)

>>> s1 = 'somestrdaasd ad ad asd as dasddsg,dlfg ,;dflg, dfg a'
>>> b1 = 'somestrdaasd ad ad asd as dasddsg,dlfg ,;dflg, dfg a'
>>> s1 == b1, s1 is b1, id(s1), id(b1)
(True, False, 4555308176, 4555308272)

Questo è inaspettato.


È successo su questo - d'accordo, che ancora più strano. Quindi ci ho giocato, ed è ancora più strano - legato allo spazio. Ad esempio, la stringa 'xx'è come prevista, così come è 'xxx', ma 'x x'non lo è.
Brian,

2
Questo perché sembra un simbolo se non c'è spazio. I nomi vengono automaticamente internati, quindi se c'è qualcosa chiamato in xxqualsiasi punto della sessione di Python, quella stringa è già internata; e potrebbe esserci un euristico che lo fa se ricorda semplicemente un nome. Come per i numeri, questo può essere fatto perché sono immutabili. docs.python.org/2/library/functions.html#intern guilload.com/python-string-interning
Yann Vernier

3

Novità di Python 3.8: Modifiche al comportamento di Python :

Il compilatore ora produce un SyntaxWarning quando i controlli di identità ( ise is not) sono usati con alcuni tipi di valori letterali (ad esempio stringhe, ints). Spesso possono funzionare per caso in CPython, ma non sono garantiti dalle specifiche del linguaggio. L'avviso avvisa gli utenti di utilizzare invece i test di uguaglianza ( == e !=).

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.