Ir al contenido principal

Ralsina.Me — El sitio web de Roberto Alsina

The problem is is. Is it not?

Al­gu­no­s, por al­gu­na ra­zó­n, ha­cen es­to:

>>> a = 2
>>> b = 2
>>> a == b
True
>>> a is b
True

Y des­pué­s, cuan­do ven es­to, se sor­pren­den:

>>> a = 1000
>>> b = 1000
>>> a == b
True
>>> a is b
False

Se sorprenden porque "2 es 2" es más intuitivo que "1000 no es 1000". Podría atribuirlo a una tendencia innata al platonismo, pero en realidad es porque is no es eso.

El operador is es (en CPython) apenas una comparación de direcciones de memoria. Si los objetos a y b son el mismo cacho de memoria, entonces "son" el otro. Como python crea de antemano una cantidad de enteros pequeños, cada 2 que creás no es un nuevo 2, sino otra vez el 2 de la última vez.

Es­to fun­cio­na por dos mo­ti­vo­s:

  1. Los en­­te­­ros son so­­­lo le­c­­tu­­ra. Po­­­dés te­­ner mu­­chas va­­ria­­bles que "co­n­­tie­­nen" el mis­­mo 2, po­r­­que no lo pue­­den ro­m­­pe­­r.

  2. En py­tho­n, la asig­na­ción es tan só­lo crear alia­ses. No se ha­ce una co­pia de 2 cuan­do se ha­ce a = 2, so­la­men­te se di­ce "a es otro nom­bre pa­ra es­te 2 que ten­go acá".

Esto sorprende a la gente que viene de otros lenguajes, por ejemplo C o C++. En esos lenguajes, una variable int a nunca usaría la misma memoria que int b porque justamente, una variable es un pedazo de memoria, y se puede cambiar el contenido. En C y C++, los enteros son mutables. Este 2 no es ese 2, a menos que lo hagas intencionalmente con punteros.

De he­cho, la for­ma en que la asig­na­ción fun­cio­na en py­thon lle­va a otras sor­pre­sas que son más in­te­re­san­tes en la vi­da rea­l. Por ejem­plo:

>>> def f(s=""):
...     s+='x'
...     return s
...
>>> f()
'x'
>>> f()
'x'
>>> f()
'x'

Eso no sor­pren­de na­da. Aho­ra, ha­ga­mos un pe­que­ño cam­bio:

>>> def f(l=[]):
...     l.append('x')
...     return l
...
>>> f()
['x']
>>> f()
['x', 'x']
>>> f()
['x', 'x', 'x']

Y eso sí es sorprendente, si no lo esperabas. Sucede porque las listas son mutables. El argumento por default se define cuando la función se define, y cada vez que llamás f() estás usando y devolviendo la misma l. Antes, también usábamos siempre la misma s pero como los strings son inmutables, nunca cambiaba, y devolvíamos una nueva cada vez.

Podés comprobar que no te miento, obviamente que usando is. Y ya que estamos, eso no es un problema para listas. Es un problema para los objetos de cualquier clase que vos definas, a menos que los hagas inmutables. Así que seamos cuidadosos con los argumentos por defecto, ¿ok?

Volviendo al problema original de que 1000 is not 1000, lo sorprendente es que en realidad, no es interesante. Los enteros son fungibles. No te importa que sea el mismo entero, solo que sean iguales.

Com­pro­bar iden­ti­dad de en­te­ros es co­mo si me pres­ta­ras $1 y cuan­do te lo de­vuel­vo, en vez de ver si es una mo­ne­da de $1, te fi­ja­ras si es la mis­ma mo­ne­da. Sim­ple­men­te no im­por­ta. Lo que que­res es un 2, un 1000 o una mo­ne­da de $1.

Además, el reultado de 2 is 2 depende de la implementación de python. No hay motivo, en realidad, mas allá de una optimización, para que sea True.

Es­pe­ran­do que es­to acla­re el te­ma, les de­jo un úl­ti­mo frag­men­to de có­di­go:

.. code-block:: pycon
>>> a = float('NaN')
>>> a is a
True
>>> a == a
False

UP­DA­TE: Mu­chos co­men­ta­rios ite­re­san­tes en re­ddit y una con­ti­nua­ción chi­qui­ta acá

toyg / 2012-01-30 11:00:

This is actually an issue in PyCharm, the JetBrains IDE for Python, which (from what I can see) will always nudge you to use "is" for any integer comparison, as it's "more readable" (which it is, but correctness will always trump readability).

BTW, the "bunch of small integers" is exactly 256, at least on Windows.

Benjamin Kloster / 2012-01-31 07:12:

I use PyCharm extensively, and I've never seen it suggest the "is" operator for integer comparison.

toyg / 2012-01-31 09:30:

maybe you turned off some option? i'm pretty sure it does suggest it. EDIT: see http://lateral.netmanagers....

Guest / 2012-01-31 16:58:

I think you're talking about the use of "and" or "or" instead of "&&" and "||".

toyg / 2012-01-31 23:14:

Actually, I went back to check and I think what I meant is the warning "boolean variable check can be simplified" (under Settings -> Inspectors -> Python), i.e. if int(someVar) == 0
In those cases, PyCharm suggests to switch from == to "is".

John Lenton / 2012-01-30 14:19:

Remind me not to lend you $1... :-)

jjconti / 2012-01-30 20:45:

Jaja, el último snippet es como un remate de standup.

vegai / 2012-01-31 08:40:

What's the benefit of pre-creating all 8-bit integers?

Roberto Alsina / 2012-01-31 17:48:

You use them often, and creating objects is kinda expensive.

shabbyrobe / 2012-01-31 12:21:

This is an excellent illustration of two of the most eye-popping WTFs Python has to offer. For a language that potentially has so much to offer beginners, total catastrophe gotchas like these simply shouldn't exist. A lot of us may well have a computer science background, but a language that comes so close to being incredibly useful without one shouldn't make it a requirement.

Roberto Alsina / 2012-01-31 17:49:

While I would not have called is "is", there is really little to be done about the mutable default argument issue without making it less useful, AFAICS


Contents © 2000-2023 Roberto Alsina