Compreendendo o operador “é” do Python

109

O isoperador não corresponde aos valores das variáveis, mas às próprias instâncias.

O que isso realmente significa?

Declarei duas variáveis ​​nomeadas xe yatribuindo os mesmos valores em ambas as variáveis, mas retorna falso quando uso o isoperador.

Eu preciso de um esclarecimento. Aqui está meu código.

x = [1, 2, 3]
y = [1, 2, 3]

print(x is y)  # It prints false!
aniskhan001
fonte
Pergunta relacionada stackoverflow.com/questions/38189660/…
Kasravnd de

Respostas:

181

Você entendeu mal o que o isoperador testa. Testa se duas variáveis ​​apontam para o mesmo objeto , não se duas variáveis ​​têm o mesmo valor.

Da documentação para o isoperador :

Os operadores ise o is notteste de identidade do objeto: x is yé verdadeiro se e somente se xe yforem o mesmo objeto.

Em ==vez disso, use o operador:

print(x == y)

Isso imprime True. xe ysão duas listas separadas :

x[0] = 4
print(y)  # prints [1, 2, 3]
print(x == y)   # prints False

Se você usar a id()função, verá isso xe yterá identificadores diferentes:

>>> id(x)
4401064560
>>> id(y)
4401098192

mas se você atribuísse ya xentão ambos apontariam para o mesmo objeto:

>>> x = y
>>> id(x)
4401064560
>>> id(y)
4401064560
>>> x is y
True

e ismostra que ambos são o mesmo objeto, ele retorna True.

Lembre-se de que em Python, os nomes são apenas rótulos que fazem referência a valores ; você pode ter vários nomes apontando para o mesmo objeto. isinforma se dois nomes apontam para um mesmo objeto. ==informa se dois nomes se referem a objetos que têm o mesmo valor.

Martijn Pieters
fonte
13
Então, A is Bé o mesmo que id(A) == id(B).
imallett
2
@imallett: esse é um proxy para o mesmo teste, desde que você não armazene id(A)em uma variável e depois espere variable == id(B)que ainda funcione; se Afoi excluído nesse ínterim, Bpoderia ter recebido o mesmo local de memória.
Martijn Pieters
1
Não foi possível formatar nos comentários. Mas há uma coisa interessante. :) >>> x = 5 \n>>> y = 5 \n>>> x é y \nVerdadeiro \n>>> x == y \nVerdadeiro \n>>>\n
Haranadh
5
Inteiros pequenos são internados no CPython, pois são usados ​​com frequência. É uma otimização. x = 5; y = 5; x é y => Verdadeiro porque id (x) == id (y). É o mesmo objeto inteiro que é reutilizado. Funciona em Python, pois os inteiros são imutáveis. Se você fizer x = 1,0; y = 1,0 ou x = 9999; y = 9999, não será a mesma identidade, porque floats e ints maiores não estão internados.
Magnus Lyckå
1
@ MagnusLyckå existem outras otimizações que podem fazer com que objetos imutáveis ​​sejam armazenados em cache. Por exemplo, se você executar seu exemplo em uma nova função ou junto com um ponto-e-vírgula de separação no interpretador interativo, você descobrirá que eles têm o mesmo id também.
Martijn Pieters
60

Outra duplicata estava perguntando por que duas strings iguais geralmente não são idênticas, o que realmente não foi respondido aqui:

>>> x = 'a' 
>>> x += 'bc'
>>> y = 'abc'
>>> x == y
True
>>> x is y
False

Então, por que eles não são a mesma string? Especialmente por causa disso:

>>> z = 'abc'
>>> w = 'abc'
>>> z is w
True

Vamos adiar a segunda parte um pouco. Como o primeiro pode ser verdade?

O intérprete teria que ter uma "tabela interna", uma tabela que mapeia valores de string para objetos de string, então toda vez que você tenta criar uma nova string com o conteúdo 'abc', você obtém o mesmo objeto. A Wikipedia tem uma discussão mais detalhada sobre como funciona o estágio.

E o Python tem uma mesa de internação de strings; você pode internar strings manualmente com o sys.internmétodo.

Na verdade, o Python tem permissão para internar automaticamente qualquer tipo imutável, mas não é obrigado a fazer isso. Implementações diferentes irão internalizar valores diferentes.

CPython (a implementação que você está usando se não sabe qual implementação está usando) internaliza automaticamente pequenos inteiros e alguns singletons especiais como False, mas não strings (ou inteiros grandes, ou tuplas pequenas, ou qualquer outra coisa). Você pode ver isso facilmente:

>>> a = 0
>>> a += 1
>>> b = 1
>>> a is b
True
>>> a = False
>>> a = not a
>>> b = True
a is b
True
>>> a = 1000
>>> a += 1
>>> b = 1001
>>> a is b
False

OK, mas por que eram ze widênticos?

Esse não é o intérprete internando automaticamente, mas sim os valores de dobramento do compilador.

Se a mesma seqüência de tempo de compilação aparece duas vezes no mesmo módulo (o que exatamente isto significa é difícil de definir-não é a mesma coisa que um literal de cadeia, porque r'abc', 'abc'e 'a' 'b' 'c'são todos literais diferentes, mas a mesma cadeia, mas fácil de entender intuitivamente), o compilador criará apenas uma instância da string, com duas referências.

Na verdade, o compilador pode ir ainda mais longe: 'ab' + 'c'pode ser convertido para 'abc'pelo otimizador, caso em que pode ser dobrado junto com uma 'abc'constante no mesmo módulo.

Novamente, isso é algo que o Python é permitido, mas não obrigatório. Mas, neste caso, CPython sempre dobra pequenas strings (e também, por exemplo, pequenas tuplas). (Embora o compilador instrução por instrução do interpretador interativo não execute a mesma otimização que o compilador módulo por vez, então você não verá exatamente os mesmos resultados interativamente.)


Então, o que você deve fazer a respeito disso como programador?

Bem ... nada. Você quase nunca tem motivos para se importar se dois valores imutáveis ​​são idênticos. Se você quiser saber quando pode usar em a is bvez de a == b, você está fazendo a pergunta errada. Use sempre, a == bexceto em dois casos:

  • Para obter comparações mais legíveis com os valores singleton, como x is None.
  • Para valores mutáveis, quando você precisa saber se a mutação xafetará o y.
abarnert
fonte
1
Excelente explicação, especialmente seu conselho no final.
DavidG
Obrigado por essa explicação detalhada. Alguém sabe: se we zsão idênticos por causa dos valores de dobramento do compilador, por que isso também funciona no REPL, mesmo usando id()para verificar as referências? Usando o REPL no Python 3.7
Chi-chi
8

issó retorna verdadeiro se eles forem realmente o mesmo objeto. Se eles fossem iguais, uma mudança em um também apareceria no outro. Aqui está um exemplo da diferença.

>>> x = [1, 2, 3]
>>> y = [1, 2, 3]
>>> print x is y
False
>>> z = y
>>> print y is z
True
>>> print x is z
False
>>> y[0] = 5
>>> print z
[5, 2, 3]
Mark Ransom
fonte
8

Provocado por uma pergunta duplicada , esta analogia pode funcionar:

# - Darling, I want some pudding!
# - There is some in the fridge.

pudding_to_eat = fridge_pudding
pudding_to_eat is fridge_pudding
# => True

# - Honey, what's with all the dirty dishes?
# - I wanted to eat pudding so I made some. Sorry about the mess, Darling.
# - But there was already some in the fridge.

pudding_to_eat = make_pudding(ingredients)
pudding_to_eat is fridge_pudding
# => False
Amadan
fonte
3
Pode ser apenas gosto pessoal (sem trocadilhos), mas achei essa analogia mais confusa do que útil e me fez querer comer pudim quando não tenho nenhum na minha geladeira :( Acho que a resposta de Mark Ransom, embora mais chata, é provavelmente mais instrutivo
Tom Close
1
@TomClose: Existem muitas respostas boas para essa pergunta, o suficiente para que haja espaço para leviandade. Além disso, também quero pudim.
Amadan
5

is e is not são os dois operadores de identidade em Python. isoperador não compara os valores das variáveis, mas compara as identidades das variáveis. Considere isto:

>>> a = [1,2,3]
>>> b = [1,2,3]
>>> hex(id(a))
'0x1079b1440'
>>> hex(id(b))
'0x107960878'
>>> a is b
False
>>> a == b
True
>>>

O exemplo acima mostra que a identidade (também pode ser o endereço de memória no Cpython) é diferente para ambos a e b(embora seus valores sejam os mesmos). É por isso que quando você diz que a is bele retorna falso devido à incompatibilidade nas identidades de ambos os operandos. No entanto, quando você diz a == b, ele retorna verdadeiro porque o== operação apenas verifica se ambos os operandos têm o mesmo valor atribuído a eles.

Exemplo interessante (para a nota extra):

>>> del a
>>> del b
>>> a = 132
>>> b = 132
>>> hex(id(a))
'0x7faa2b609738'
>>> hex(id(b))
'0x7faa2b609738'
>>> a is b
True
>>> a == b
True
>>>

No exemplo acima, embora ae bsejam duas variáveis ​​diferentes, a is bretornado True. Isso ocorre porque o tipo de aé intque é um objeto imutável. Então python (eu acho que para economizar memória) alocou o mesmo objeto para bquando foi criado com o mesmo valor. Portanto, neste caso, as identidades das variáveis ​​corresponderam e a is bacabaram por ser True.

Isso se aplica a todos os objetos imutáveis:

>>> del a
>>> del b
>>> a = "asd"
>>> b = "asd"
>>> hex(id(a))
'0x1079b05a8'
>>> hex(id(b))
'0x1079b05a8'
>>> a is b
True
>>> a == b
True
>>>

Espero que ajude.

gixxer
fonte
este é um exemplo muito bom. obrigado por informações detalhadas.
Haranadh
Mas tente a = 123456789 b = 123456789
user2183078 05 de
Tudo menos do que -5ou maior do que 256em Python será False. Python armazena números em cache no intervalo [-5, 256].
smart
Nem todos os objetos imutáveis ​​serão compartilhados como você mostra; essa é uma otimização aplicada pelo tempo de execução do Python para alguns objetos, mas não para outros. O processo de compartilhamento de pequenos inteiros está bem documentado, mas não acho que seja para internação de strings .
Mark Ransom
4

x is yé o mesmo que id(x) == id(y)comparar a identidade de objetos.

Como @ tomasz-kurgan apontou no comentário abaixo is operador se comporta de maneira incomum com certos objetos.

Por exemplo

>>> class A(object):
...   def foo(self):
...     pass
... 
>>> a = A()
>>> a.foo is a.foo
False
>>> id(a.foo) == id(a.foo)
True

Ref;
https://docs.python.org/2/reference/expressions.html#is-not
https://docs.python.org/2/reference/expressions.html#id24

Nizam Mohamed
fonte
Não, não importa. Ele pode se comportar de forma semelhante na maioria dos casos, mas nem sempre é verdade. Veja isto - no final da página, item 6 .:> (...), você pode notar um comportamento aparentemente incomum em certos usos do operador is , como aqueles envolvendo comparações entre métodos de instância ou constantes e o exemplo de trabalho mínimo : `classe A (objeto): def foo (self): pass a = A () print a.foo é a.foo print id (a.foo) == id (a.foo)`
Tomasz Kurgan
3

Como você pode verificar aqui, para pequenos números inteiros. Números acima de 257 não são pequenos, por isso é calculado como um objeto diferente.

É melhor usar ==nesse caso.

Mais informações estão aqui: http://docs.python.org/2/c-api/int.html

CS Gamer
fonte
2

X aponta para uma matriz, Y aponta para uma matriz diferente. Essas matrizes são idênticas, mas o isoperador olhará para esses ponteiros, que não são idênticos.

Neko
fonte
5
Python não tem ponteiros. Você precisa restringir sua terminologia.
David Heffernan
3
Ele o faz internamente, assim como Java e muitas outras linguagens. Na verdade, a isfuncionalidade do operador mostra isso.
Neko
5
Os detalhes da implementação não são o que importa. A documentação usa a terminologia "identidade do objeto". Você também deveria. "Os operadores são e não testam a identidade do objeto: x é y é verdadeiro se e somente se x e y são o mesmo objeto. X não é y produz o valor verdade inverso."
David Heffernan
1
@Neko: CPython usa ponteiros internamente. Mas obviamente Jython (implementado em Java) e PyPy (implementado em um subconjunto de Python) não usam ponteiros. No PyPy, alguns objetos nem terão um, a idmenos que você peça por ele.
abarnert de
1

Ele compara a identidade do objeto, ou seja, se as variáveis ​​se referem ao mesmo objeto na memória. É como ==em Java ou C (ao comparar ponteiros).

mipadi
fonte
1

Um exemplo simples com frutas

fruitlist = [" apple ", " banana ", " cherry ", " durian "]
newfruitlist = fruitlist
verynewfruitlist = fruitlist [:]
print ( fruitlist is newfruitlist )
print ( fruitlist is verynewfruitlist )
print ( newfruitlist is verynewfruitlist )

Resultado:

True
False
False

Se você tentar

fruitlist = [" apple ", " banana ", " cherry ", " durian "]
newfruitlist = fruitlist
verynewfruitlist = fruitlist [:]
print ( fruitlist == newfruitlist )
print ( fruitlist == verynewfruitlist )
print ( newfruitlist == verynewfruitlist )

O resultado é diferente:

True
True
True

Isso ocorre porque o operador == compara apenas o conteúdo da variável. Para comparar as identidades de 2 variáveis, use o operador is

Para imprimir o número de identificação:

print ( id( variable ) )
ForsakenOne
fonte
-3

O isoperador nada mais é do que uma versão em inglês do ==. Como os IDs das duas listas são diferentes, a resposta é falsa. Podes tentar:

a=[1,2,3]
b=a
print(b is a )#True

* Porque os IDs de ambas as listas seriam os mesmos

Aadit
fonte
isnão é 'uma versão em inglês de =='
David Buck