¿Para qué se utiliza la función id ()?

99

Leí los documentos de Python 2 y noté la id()función:

Devuelve la "identidad" de un objeto. Este es un número entero (o entero largo) que se garantiza que es único y constante para este objeto durante su vida útil. Dos objetos con vidas útiles que no se superponen pueden tener el mismo valor de id ().

Detalle de implementación de CPython: esta es la dirección del objeto en la memoria.

Entonces, experimenté usando id()con una lista:

>>> list = [1,2,3]
>>> id(list[0])
31186196
>>> id(list[1])
31907092 // increased by 896
>>> id(list[2])
31907080 // decreased by 12

¿Cuál es el número entero devuelto por la función? ¿Es también una dirección de memoria en C? Si es así, ¿por qué el número entero no corresponde al tamaño del tipo de datos?

¿Cuándo se id()usa en la práctica?

Thanakron Tandavas
fuente
4
El hecho de que almacene (digamos) un int de 32 bits en una estructura de datos en un lenguaje de scripting no significa que estará usando hasta 32 bits más de memoria. SIEMPRE hay metadatos adjuntos a CUALQUIER dato que almacene. tipo, tamaño, longitud, bla, bla, bla.
Marc B
cpython asigna desde un montón que se revuelve a medida que los objetos se mallocalizan y liberan.
tdelaney
Los números de Python no son simples datos. Son objetos que utilizan longs internamente para empezar, luego se auto-promueven a una representación de estilo BigNumber si el valor es demasiado grande.
Gareth Latty

Respuestas:

156

Tu publicación hace varias preguntas:

¿Cuál es el número devuelto por la función?

Es " un entero (o entero largo) que se garantiza que es único y constante para este objeto durante su vida " . (Biblioteca estándar de Python - Funciones integradas) Un número único. Nada más y nada menos. Piense en ello como un número de seguridad social o un número de identificación de empleado para los objetos de Python.

¿Es lo mismo con las direcciones de memoria en C?

Conceptualmente, sí, en el sentido de que se garantiza que ambos serán únicos en su universo durante su vida. Y en una implementación particular de Python, en realidad es la dirección de memoria del objeto C correspondiente.

Si es así, ¿por qué el número no aumenta instantáneamente por el tamaño del tipo de datos (supongo que sería int)?

Porque una lista no es una matriz y un elemento de lista es una referencia, no un objeto.

¿Cuándo usamos realmente la id( )función?

Casi nunca. id()(o su equivalente) se usa en el isoperador.

Robᵩ
fuente
Usamos id () generalmente con fines de demostración como aquí stackoverflow.com/questions/17246693/…
Nabin
49

Esa es la identidad de la ubicación del objeto en la memoria ...

Este ejemplo puede ayudarlo a comprender un poco más el concepto.

foo = 1
bar = foo
baz = bar
fii = 1

print id(foo)
print id(bar)
print id(baz)
print id(fii)

> 1532352
> 1532352
> 1532352
> 1532352

Todos ellos apuntan a la misma ubicación en la memoria, por lo que sus valores son los mismos. En el ejemplo, 1solo se almacena una vez, y cualquier otra cosa que apunte 1hará referencia a esa ubicación de memoria.

brbcoding
fuente
9
Pero si usa números más allá del rango de -5 a 256, no obtendrá la misma identificación para la variable fii.
saurav
eso es extremadamente interesante. ¿Puedes compartir más?
jouell
3
Creo que esta respuesta es engañosa ya que no es cierto para la mayoría de los números; El operador "es" se comporta inesperadamente con números enteros .
kevinji
8

id()devuelve la dirección del objeto al que se hace referencia (en CPython), pero su confusión proviene del hecho de que las listas de Python son muy diferentes de las matrices de C. En una lista de Python, cada elemento es una referencia . Entonces, lo que estás haciendo es mucho más similar a este código C:

int *arr[3];
arr[0] = malloc(sizeof(int));
*arr[0] = 1;
arr[1] = malloc(sizeof(int));
*arr[1] = 2;
arr[2] = malloc(sizeof(int));
*arr[2] = 3;
printf("%p %p %p", arr[0], arr[1], arr[2]);

En otras palabras, está imprimiendo la dirección de la referencia y no una dirección relativa al lugar donde se almacena su lista.

En mi caso, encontré la id()función útil para crear identificadores opacos para volver al código C cuando se llama pythondesde C. Al hacerlo, puede usar fácilmente un diccionario para buscar el objeto desde su identificador y se garantiza que es único.

Error fatal
fuente
7

La respuesta de Rob (la más votada arriba) es correcta. Me gustaría agregar que, en algunas situaciones, el uso de ID es útil ya que permite comparar objetos y encontrar qué objetos se refieren a sus objetos.

El último generalmente lo ayuda, por ejemplo, a depurar errores extraños donde los objetos mutables se pasan como parámetro para decir clases y se asignan a vars locales en una clase. La mutación de esos objetos mutará vars en una clase. Esto se manifiesta en un comportamiento extraño donde varias cosas cambian al mismo tiempo.

Recientemente tuve este problema con una aplicación Python / Tkinter donde la edición de texto en un campo de entrada de texto cambiaba el texto en otro mientras escribía :)

Aquí hay un ejemplo de cómo puede usar la función id () para rastrear dónde están esas referencias. Por supuesto, esta no es una solución que cubra todos los casos posibles, pero entiendes la idea. Nuevamente, las ID se utilizan en segundo plano y el usuario no las ve:

class democlass:
    classvar = 24

    def __init__(self, var):
        self.instancevar1 = var
        self.instancevar2 = 42

    def whoreferencesmylocalvars(self, fromwhere):
        return {__l__: {__g__
                    for __g__ in fromwhere
                        if not callable(__g__) and id(eval(__g__)) == id(getattr(self,__l__))
                    }
                for __l__ in dir(self)
                    if not callable(getattr(self, __l__)) and __l__[-1] != '_'
                }

    def whoreferencesthisclassinstance(self, fromwhere):
        return {__g__
                    for __g__ in fromwhere
                        if not callable(__g__) and id(eval(__g__)) == id(self)
                }

a = [1,2,3,4]
b = a
c = b
democlassinstance = democlass(a)
d = democlassinstance
e = d
f = democlassinstance.classvar
g = democlassinstance.instancevar2

print( 'My class instance is of', type(democlassinstance), 'type.')
print( 'My instance vars are referenced by:', democlassinstance.whoreferencesmylocalvars(globals()) )
print( 'My class instance is referenced by:', democlassinstance.whoreferencesthisclassinstance(globals()) )

SALIDA:

My class instance is of <class '__main__.democlass'> type.
My instance vars are referenced by: {'instancevar2': {'g'}, 'classvar': {'f'}, 'instancevar1': {'a', 'c', 'b'}}
My class instance is referenced by: {'e', 'd', 'democlassinstance'}

Los guiones bajos en los nombres de las variables se utilizan para evitar colisiones de nombres. Las funciones utilizan el argumento "desde donde" para que pueda hacerles saber dónde empezar a buscar referencias. Este argumento se completa con una función que enumera todos los nombres en un espacio de nombres dado. Globals () es una de esas funciones.

Arijan
fuente
¡Gran explicación!
Neeraj Verma
5

Estoy comenzando con Python y uso id cuando uso el shell interactivo para ver si mis variables están asignadas a lo mismo o si simplemente se ven iguales.

Cada valor es una identificación, que es un número único relacionado con el lugar donde se almacena en la memoria de la computadora.

lontgomjr
fuente
5

Si está utilizando Python 3.4.1, obtendrá una respuesta diferente a su pregunta.

list = [1,2,3]
id(list[0])
id(list[1])
id(list[2])

devoluciones:

1705950792   
1705950808  # increased by 16   
1705950824  # increased by 16

Los números enteros -5deben 256tener una identificación constante, y al encontrarla varias veces, su identificación no cambia, a diferencia de todos los demás números anteriores o posteriores que tienen diferentes identificaciones cada vez que la encuentra. Los números de -5para 256tener identificaciones en orden creciente y difieren en 16.

El número devuelto por la id()función es una identificación única que se le da a cada elemento almacenado en la memoria y, por analogía, es el mismo que la ubicación de la memoria en C.

Dhvanan Shah
fuente
2

La respuesta es prácticamente nunca. Los ID se utilizan principalmente de forma interna en Python.

El programador medio de Python probablemente nunca necesitará utilizar id()en su código.

Gareth Latty
fuente
3
Quizás no soy promedio, pero uso id()bastante. Dos casos de uso que se me vienen a la cabeza: un dictado de identidad (hecho a mano, ad-hoc) y una costumbre repr()para un objeto donde la identidad importa pero el valor predeterminado reprno es apropiado.
5
@delnan No diría que esos son casos comunes.
Gareth Latty
2

El isoperador lo usa para verificar si dos objetos son idénticos (en oposición a iguales). El valor real que se devuelve id()prácticamente nunca se usa para nada porque realmente no tiene un significado y depende de la plataforma.

omz
fuente
2

Es la dirección del objeto en la memoria, exactamente como dice el documento. Sin embargo, tiene metadatos adjuntos, las propiedades del objeto y la ubicación en la memoria son necesarias para almacenar los metadatos. Entonces, cuando crea su variable llamada lista, también crea metadatos para la lista y sus elementos.

Entonces, a menos que sea un gurú absoluto en el idioma, no puede determinar la identificación del siguiente elemento de su lista en función del elemento anterior, porque no sabe qué asigna el idioma junto con los elementos.

Lajos Arpad
fuente
1
En realidad, ser un gurú de Python no será de mucha utilidad para predecir los objetos id(). Debería estar íntimamente familiarizado con los administradores de memoria (¡plural!) En cuestión, conocer su estado exacto en algún momento y conocer el orden exacto en el que se han asignado los objetos. En otras palabras: no va a pasar.
Eres un gurú absoluto de Python si conoces absolutamente todos sus matices, incluidos los posibles administradores de memoria.
Lajos Arpad
1
Conocer Python y conocer una implementación en particular (por ejemplo, CPython) son dos cosas completamente diferentes. E incluso conocer CPython de adentro hacia afuera no ayudará, ya que hay varios administradores de memoria a los que CPython llama que no son parte de CPython sino del sistema operativo respectivo. Y como he dicho antes, incluso saber todas estas cosas que no son de Python no le ayudará, ya que las direcciones reales dependen del estado de tiempo de ejecución de los administradores de memoria y, a veces, incluso son aleatorias.
1
Sin mencionar que sería inútil saberlo, ya que confiar en tal cosa sería depender de un detalle de implementación y, por lo tanto, sería frágil e inflexible.
Gareth Latty
2

Tengo una idea para utilizar el valor id()en el registro.
Es barato de conseguir y es bastante corto.
En mi caso, uso tornado y me id()gustaría tener un ancla para agrupar mensajes dispersos y mezclados entre archivos por conector web.

Daneel S. Yaitskov
fuente
0

A partir de Python 3, la identificación se asigna a un valor, no a una variable. Esto significa que si crea dos funciones como se muestra a continuación, las tres identificaciones son iguales.

>>> def xyz():
...     q=123
...     print(id(q))
...
>>> def iop():
...     w=123
...     print(id(w))
>>> xyz()
1650376736
>>> iop()
1650376736
>>> id(123)
1650376736
Asish Kumar
fuente
Aunque esto contradice explicaciones anteriores, parece suceder y no sé por qué. ¿Alguna explicación? Por ejemplo, x [0] = "abc" x [1] = "def" q = ["abc", "def"]. Id (x) == Id (q) es verdadero. ¿Coincidencia?
VMMF
0

Llego un poco tarde y hablaré de Python3. Para comprender qué es id () y cómo funciona (y Python), considere el siguiente ejemplo:

>>> x=1000
>>> y=1000
>>> id(x)==id(y)
False
>>> id(x)
4312240944
>>> id(y)
4312240912
>>> id(1000)
4312241104
>>> x=1000
>>> id(x)
4312241104
>>> y=1000
>>> id(y)
4312241200

Debes pensar en todo lo que está en el lado derecho como objetos. Cada vez que realiza una asignación, crea un nuevo objeto y eso significa una nueva identificación. En el medio puede ver un objeto "salvaje" que se crea solo para la función - id (1000). Entonces, su vida útil es solo para esa línea de código. Si marca la siguiente línea, verá que cuando creamos una nueva variable x, tiene la misma identificación que ese objeto salvaje. Prácticamente funciona como una dirección de memoria.

Tomas
fuente
0

Tenga cuidado (con respecto a la respuesta que se encuentra a continuación) ... Eso solo es cierto porque 123 está entre -5 y 256 ...

In [111]: q = 257                                                         

In [112]: id(q)                                                            
Out[112]: 140020248465168

In [113]: w = 257                                                         

In [114]: id(w)                                                           
Out[114]: 140020274622544

In [115]: id(257)                                                         
Out[115]: 140020274622768
Caramba
fuente