Por que comparar seqüências de caracteres usando '==' ou 'is' às vezes produz um resultado diferente?

1147

Eu tenho um programa Python onde duas variáveis ​​são definidas para o valor 'public'. Em uma expressão condicional, tenho a comparação var1 is var2que falha, mas se eu a alterar, var1 == var2ela retornará True.

Agora, se eu abrir meu interpretador Python e fizer a mesma comparação "is", ele será bem-sucedido.

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

O que estou perdendo aqui?

jottos
fonte
8
Veja: stackoverflow.com/questions/1392433/…
Nick Dandoulakis
3
Esse problema também ocorre quando você lê uma entrada de consola através, por exemplo: input = raw_input("Decide (y/n): "). Nesse caso, uma entrada de "y" e if input == 'y':retornará "True" enquanto if input is 'y':retornará False.
Semjon Mössinger
4
Este blog fornece uma explicação muito mais completa do que qualquer resposta guilload.com/python-string-interning
Chris_Rands
1
Como @ chris-rico menciona, eu ótima explicação aqui stackoverflow.com/q/15541404/1695680
ThorSummoner

Respostas:

1533

isé teste de identidade, ==é teste de igualdade. o que acontece no seu código seria emulado no intérprete assim:

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

então, não é de admirar que eles não sejam os mesmos, certo?

Em outras palavras: isé oid(a) == id(b)

SilentGhost
fonte
17
ahh mesmo que eq? vs igual? no esquema, entendi.
Jottos
47
Ou ==vs .equals()em Java. A melhor parte é que o Python ==não é análogo ao Java ==.
MatrixFrog
11
@ Крайст: existe apenas um único Nonevalor. Portanto, sempre tem o mesmo ID.
SilentGhost
18
Isso não aborda o exemplo "is -> True" do OP.
user2864740
6
@AlexanderSupertramp, por causa da internação de strings .
Chris Rico
570

Outras respostas aqui estão corretas: isé usada para comparação de identidade , enquanto ==é usada para comparação de igualdade . Como você se preocupa com a igualdade (as duas cadeias devem conter os mesmos caracteres), nesse caso, o isoperador está simplesmente errado e você deve usá-lo ==.

A razão pela qual isfunciona interativamente é que (a maioria) literais de string são internados por padrão. Da Wikipedia:

As cadeias internas aceleram as comparações de cadeias, que às vezes são um gargalo de desempenho em aplicativos (como compiladores e tempos de execução da linguagem de programação dinâmica) que dependem muito de tabelas de hash com chaves de cadeia. Sem internar, verificar se duas cadeias diferentes são iguais envolve examinar todos os caracteres das duas cadeias. Isso é lento por vários motivos: é inerentemente O (n) no comprimento das cadeias; normalmente requer leituras de várias regiões da memória, o que leva tempo; e as leituras preenchem o cache do processador, o que significa que há menos cache disponível para outras necessidades. Com cadeias internas, um teste simples de identidade de objeto é suficiente após a operação interna original; isso geralmente é implementado como um teste de igualdade de ponteiro,

Portanto, quando você tem duas literais de string (palavras que são literalmente digitadas no código-fonte do programa, entre aspas) no programa que possuem o mesmo valor, o compilador Python internará automaticamente as strings, tornando-as armazenadas da mesma forma localização da memória. (Observe que isso nem sempre acontece, e as regras para quando isso acontece são bastante complicadas; portanto, não confie nesse comportamento no código de produção!)

Como na sua sessão interativa, ambas as seqüências são realmente armazenadas no mesmo local de memória, elas têm a mesma identidade , portanto o isoperador trabalha conforme o esperado. Mas se você construir uma string por outro método (mesmo que ela contenha exatamente os mesmos caracteres), a string poderá ser igual , mas não será a mesma string - ou seja, ela terá uma identidade diferente , porque é armazenado em um local diferente na memória.

Daniel Pryden
fonte
6
Onde alguém pode ler mais sobre as regras complicadas para quando as strings são internadas?
Noctis Skytower 11/04
89
+1 para uma explicação completa. Não tenho certeza de como a outra resposta recebeu tantos votos positivos sem explicar o que realmente aconteceu.
precisa saber é o seguinte
4
Foi exatamente isso que pensei quando li a pergunta. A resposta aceita é curta, mas contém o fato, mas essa resposta explica as coisas muito melhor. Agradável!
Sнаđошƒаӽ
3
@NoctisSkytower pesquisou no Google o mesmo e encontrou este guilload.com/python-string-interning
xtreak
5
@ naught101: Não, a regra é escolher entre ==e com isbase no tipo de verificação que você deseja. Se você se preocupa com as seqüências de caracteres iguais (ou seja, com o mesmo conteúdo), use sempre ==. Se você se importa se dois nomes de Python se referem à mesma instância de objeto, você deve usar is. Pode ser necessário isse você estiver escrevendo um código que lide com muitos valores diferentes sem se importar com o conteúdo deles, ou se souber que existe apenas um de algo e desejar ignorar outros objetos que fingem ser esse. Se você não tiver certeza, sempre escolha ==.
Daniel Pryden
108

A ispalavra-chave é um teste para a identidade do objeto, enquanto ==é uma comparação de valores.

Se você usar is, o resultado será verdadeiro se e somente se o objeto for o mesmo objeto. No entanto, ==será verdade sempre que os valores do objeto forem os mesmos.

Thomas Owens
fonte
57

Uma última coisa a observar, você pode usar a sys.internfunção para garantir que você esteja obtendo uma referência para a mesma string:

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

Como indicado acima, você não deve usar ispara determinar a igualdade de cadeias. Mas isso pode ser útil para saber se você tem algum tipo de requisito estranho de usar is.

Observe que a internfunção costumava ser embutida no Python 2, mas foi movida para o sysmódulo no Python 3.

Jason Baker
fonte
43

isé teste de identidade, ==é teste de igualdade. O que isso significa é que isé uma maneira de verificar se duas coisas são iguais ou apenas equivalentes.

Digamos que você tenha um personobjeto simples . Se ele é chamado de 'Jack' e tem '23' anos, é equivalente a outro Jack de 23 anos, mas não é a mesma pessoa.

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

Eles têm a mesma idade, mas não são a mesma instância de pessoa. Uma string pode ser equivalente a outra, mas não é o mesmo objeto.

TankorSmash
fonte
Se você alterar o conjunto jack1.age = 99, isso não mudará jack2.age. Isso ocorre porque são duas instâncias diferentes jack1 is not jack2. No entanto, eles podem se igualar jack1 == jack2se o nome e a idade forem iguais. Fica mais complicado para as strings, porque as strings são imutáveis ​​no Python, e o Python geralmente reutiliza a mesma instância. Eu gosto dessa explicação porque ela usa os casos simples (um objeto normal) e não os casos especiais (strings).
Flimm
37

Esta é uma observação lateral, mas no python idiomático, você verá frequentemente coisas como:

if x is None: 
    # some clauses

Isso é seguro, porque é garantido que haja uma instância do Objeto Nulo (ou seja, Nenhum) .

Gregg Lind
fonte
1
O mesmo vale para Verdadeiro e Falso? Apenas uma instância será igual a?
HandyManDan
1
@HandyManDan Sim, eles são únicos, tanto em python 2 e 3.
kamillitw
@ kamillitw mas no Python 2 você pode reatribuir False e True.
Martijn Pieters
28

Se você não tiver certeza do que está fazendo, use o '=='. Se você tiver um pouco mais de conhecimento, poderá usar 'is' para objetos conhecidos como 'None'.

Caso contrário, você acabará se perguntando por que as coisas não funcionam e por que isso acontece:

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

Não tenho certeza se algumas coisas são garantidas para permanecerem iguais entre diferentes versões / implementações do python.

Mattias Nilsson
fonte
1
Exemplo interessante que mostra como a reatribuição de ints aciona essa condição. Por que isso falhou? É devido a internação ou algo mais?
Paul
Parece que o motivo pelo qual o retorno é false pode ser devido à implementação do interpretador: stackoverflow.com/questions/132988/…
Paul
@ArchitJain Sim, esses links explicam muito bem. Ao lê-los, você saberá em que números você pode usar 'está'. Eu só gostaria que eles explicassem por que ainda não é uma boa ideia fazer isso :) Você sabe que isso não torna uma boa ideia assumir que todo mundo faz o mesmo (ou que o intervalo de números internalizados nunca será alterado)
Mattias Nilsson
20

Da minha experiência limitada com python, isé usado para comparar dois objetos para ver se eles são o mesmo objeto, em oposição a dois objetos diferentes com o mesmo valor. ==é usado para determinar se os valores são idênticos.

Aqui está um bom exemplo:

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

s1é uma string unicode e s2é uma string normal. Eles não são do mesmo tipo, mas têm o mesmo valor.

Jack M.
fonte
17

Eu acho que tem a ver com o fato de que, quando a comparação 'é' avaliada como falsa, dois objetos distintos são usados. Se for avaliada como verdadeira, significa que internamente está usando o mesmo objeto exato e não criando um novo, possivelmente porque você os criou em uma fração de 2 segundos ou mais e porque não há um grande intervalo de tempo entre otimizado e usa o mesmo objeto.

É por isso que você deve usar o operador de igualdade ==, não ispara comparar o valor de um objeto de sequência.

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

Neste exemplo, criei s2, que era um objeto de string diferente anteriormente igual a 'one', mas não é o mesmo objeto sporque, porque o intérprete não usou o mesmo objeto que eu não o atribua inicialmente a 'one', se eu tivesse, teria feito do mesmo objeto.

meder omuraliev
fonte
3
Usar .replace()como exemplo neste contexto provavelmente não é o melhor, porque sua semântica pode ser confusa. s2 = s2.replace()vai sempre criar um novo objeto string, atribuir o novo objeto string para s2, em seguida, descartar o objeto string que s2usado para apontar para. Portanto, mesmo que você o fizesse s = s.replace('one', 'one'), ainda receberia um novo objeto de string.
1011 Daniel Pryden
13

Eu acredito que isso é conhecido como seqüências "internadas". O Python faz isso, o Java e o C e C ++ ao compilar nos modos otimizados.

Se você usar duas cadeias idênticas, em vez de desperdiçar memória criando dois objetos de cadeia, todas as cadeias internas com o mesmo conteúdo apontam para a mesma memória.

Isso resulta no operador Python "is" retornando True, porque duas cadeias com o mesmo conteúdo estão apontando para o mesmo objeto. Isso também acontecerá em Java e em C.

Isso é útil apenas para economia de memória. Você não pode confiar nele para testar a igualdade de cadeias, porque os vários intérpretes e compiladores e mecanismos JIT nem sempre podem fazê-lo.

Zan Lynx
fonte
12

Estou respondendo à pergunta mesmo que a pergunta seja antiga, porque nenhuma resposta acima cita a referência de idioma

Na verdade, o operador is verifica a identidade e == operador verifica a igualdade,

Referência da linguagem:

Os tipos afetam quase todos os aspectos do comportamento do objeto. Até a importância da identidade do objeto é afetada em algum sentido: para tipos imutáveis, operações que computam novos valores podem realmente retornar uma referência a qualquer objeto existente com o mesmo tipo e valor, enquanto que para objetos mutáveis ​​isso não é permitido . Por exemplo, depois de a = 1; b = 1, aeb pode ou não se referir ao mesmo objeto com o valor um, dependendo da implementação, mas depois de c = []; d = [], c e d são garantidos para se referir a duas listas vazias diferentes, exclusivas e recém-criadas. (Observe que c = d = [] atribui o mesmo objeto a c e d.)

portanto, da declaração acima, podemos inferir que as seqüências de caracteres que são do tipo imutável podem falhar quando marcadas com "é" e podem ser bem-sucedidas quando marcadas com "é"

O mesmo se aplica para int, tupla, que também são tipos imutáveis

RAM
fonte
8

A ==equivalência do valor do teste do operador. O isoperador testa a identidade do objeto, o Python testa se os dois são realmente o mesmo objeto (ou seja, vivem no mesmo endereço na memória).

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

Neste exemplo, o Python criou apenas um objeto de string e os dois ae bse refere a ele. O motivo é que o Python armazena e reutiliza internamente algumas seqüências de caracteres como uma otimização; na verdade, existe apenas uma string 'banana' na memória, compartilhada por aeb; Para disparar o comportamento normal, você precisa usar seqüências mais longas:

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

Ao criar duas listas, você obtém dois objetos:

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

Nesse caso, diríamos que as duas listas são equivalentes, porque têm os mesmos elementos, mas não são idênticas, porque não são o mesmo objeto. Se dois objetos são idênticos, eles também são equivalentes, mas se forem equivalentes, não são necessariamente idênticos.

Se ase refere a um objeto e você atribui b = a, as duas variáveis ​​se referem ao mesmo objeto:

>>> a = [1, 2, 3]
>>> b = a
>>> b is a
True
X. Wang
fonte
7

isirá comparar a localização da memória. É usado para comparação em nível de objeto.

==irá comparar as variáveis ​​no programa. É usado para verificar em um nível de valor.

is verifica a equivalência no nível do endereço

== verifica a equivalência no nível do valor

johnashu
fonte
3

isé teste de identidade, ==é teste de igualdade (consulte a documentação do Python ).

Na maioria dos casos, se a is b, então a == b. Mas há exceções, por exemplo:

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

Portanto, você só pode usar ispara testes de identidade, nunca para testes de igualdade.

Ryan
fonte