A cosa serve la funzione id ()?


99

Ho letto la documentazione di Python 2 e ho notato la id()funzione:

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

Dettagli sull'implementazione di CPython: questo è l'indirizzo dell'oggetto in memoria.

Quindi, ho sperimentato utilizzando id()un elenco:

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

Qual è il numero intero restituito dalla funzione? È anche per gli indirizzi di memoria in C? In tal caso, perché il numero intero non corrisponde alla dimensione del tipo di dati?

Quando viene id()utilizzato in pratica?


4
Solo perché memorizzi (diciamo) un int a 32 bit in una struttura dati in un linguaggio di scripting non significa che utilizzerai fino a 32 bit in più di memoria. ci sono SEMPRE metadati allegati a QUALSIASI dato che memorizzi. tipo, dimensione, lunghezza, blah blah blah.
Marc B

cpython alloca da un mucchio che viene rimescolato quando gli oggetti vengono mallocati e liberati.
tdelaney

I numeri Python non sono semplici pezzi di dati. Sono oggetti che usano i long internamente per cominciare, quindi promuovono automaticamente una rappresentazione in stile BigNumber se il valore diventa troppo grande.
Gareth Latty

Risposte:


156

Il tuo post pone diverse domande:

Qual è il numero restituito dalla funzione?

È " un numero intero (o intero lungo) che è garantito per essere unico e costante per questo oggetto durante la sua vita. " (Libreria standard Python - Funzioni incorporate) Un numero univoco. Niente di più e niente di meno. Consideralo come un numero di previdenza sociale o un numero di identificazione del dipendente per gli oggetti Python.

È lo stesso con gli indirizzi di memoria in C?

Concettualmente, sì, in quanto entrambi sono garantiti per essere unici nel loro universo durante la loro vita. E in una particolare implementazione di Python, in realtà è l'indirizzo di memoria del corrispondente oggetto C.

Se sì, perché il numero non aumenta istantaneamente della dimensione del tipo di dati (presumo che sarebbe int)?

Perché un elenco non è un array e un elemento di elenco è un riferimento, non un oggetto.

Quando usiamo davvero la id( )funzione?

Quasi mai. id()(o il suo equivalente) viene utilizzato nell'operatore is.


Usiamo id () di solito in scopo dimostrativo come qui stackoverflow.com/questions/17246693/...
Nabin

49

Questa è l'identità della posizione dell'oggetto in memoria ...

Questo esempio potrebbe aiutarti a capire un po 'di più il concetto.

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

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

> 1532352
> 1532352
> 1532352
> 1532352

Tutti puntano alla stessa posizione in memoria, motivo per cui i loro valori sono gli stessi. Nell'esempio, 1viene memorizzato solo una volta e qualsiasi altra cosa a cui punta 1farà riferimento a quella posizione di memoria.


9
Ma se usi numeri oltre l'intervallo da -5 a 256, non otterrai lo stesso id per la variabile fii.
saurav

è estremamente interessante. Puoi condividere di più?
jouell

3
Penso che questa risposta sia fuorviante poiché questo non è vero per la maggior parte dei numeri; vedere l' operatore "is" si comporta in modo imprevisto con gli interi .
kevinji

8

id()restituisce l'indirizzo dell'oggetto a cui si fa riferimento (in CPython), ma la tua confusione deriva dal fatto che gli elenchi di Python sono molto diversi dagli array C. In un elenco Python, ogni elemento è un riferimento . Quindi quello che stai facendo è molto più simile a questo codice 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]);

In altre parole, stai stampando l'indirizzo dal riferimento e non un indirizzo relativo a dove è memorizzato il tuo elenco.

Nel mio caso, ho trovato id()utile la funzione per creare maniglie opache per tornare al codice C quando si chiama pythonda C. In questo modo, puoi facilmente utilizzare un dizionario per cercare l'oggetto dalla sua maniglia ed è garantito che sia unico.


7

La risposta di Rob (la più votata sopra) è corretta. Vorrei aggiungere che in alcune situazioni l'uso degli ID è utile in quanto consente il confronto degli oggetti e la ricerca di quali oggetti si riferiscono ai propri oggetti.

L'ultimo di solito ti aiuta, ad esempio, a eseguire il debug di strani bug in cui gli oggetti mutabili vengono passati come parametri per dire classi e sono assegnati a variabili locali in una classe. La modifica di questi oggetti muterà le variabili in una classe. Questo si manifesta in uno strano comportamento in cui più cose cambiano allo stesso tempo.

Recentemente ho avuto questo problema con un'app Python / Tkinter in cui la modifica del testo in un campo di immissione del testo cambiava il testo in un altro mentre digitavo :)

Ecco un esempio di come potresti usare function id () per tracciare dove si trovano quei riferimenti. Con tutti i mezzi questa non è una soluzione che copre tutti i casi possibili, ma hai l'idea. Anche in questo caso gli ID vengono utilizzati in background e l'utente non li vede:

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()) )

PRODUZIONE:

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'}

I trattini di sottolineatura nei nomi delle variabili vengono utilizzati per prevenire la collisione dei nomi. Le funzioni utilizzano l'argomento "fromwhere" in modo che tu possa far sapere loro da dove iniziare la ricerca dei riferimenti. Questo argomento è riempito da una funzione che elenca tutti i nomi in un determinato spazio dei nomi. Globals () è una di queste funzioni.


Ottima spiegazione!
Neeraj Verma

5

Sto iniziando con python e uso id quando uso la shell interattiva per vedere se le mie variabili sono assegnate alla stessa cosa o se hanno lo stesso aspetto.

Ogni valore è un id, che è un numero univoco correlato a dove è archiviato nella memoria del computer.


5

Se stai usando python 3.4.1, otterrai una risposta diversa alla tua domanda.

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

ritorna:

1705950792   
1705950808  # increased by 16   
1705950824  # increased by 16

Gli interi -5devono 256avere un ID costante e, trovandolo più volte, il suo ID non cambia, a differenza di tutti gli altri numeri prima o dopo di esso che hanno ID diversi ogni volta che lo trovi. I numeri da -5a 256hanno ID in ordine crescente e differiscono di 16.

Il numero restituito dalla id()funzione è un ID univoco assegnato a ciascun elemento archiviato in memoria ed è analogo alla posizione di memoria in C.


2

La risposta è praticamente mai. Gli ID vengono utilizzati principalmente internamente a Python.

Il programmatore Python medio probabilmente non avrà mai bisogno di usarlo id()nel proprio codice.


3
Forse non sono nella media, ma ne uso id()parecchio. Due casi d'uso fuori dalla mia testa: un dict sull'identità (tirato a mano, ad-hoc) e un'abitudine repr()per un oggetto in cui l'identità è importante ma l'impostazione predefinita reprnon è appropriata.

5
@delnan Non direi che questi sono casi comuni.
Gareth Latty

2

L' isoperatore lo utilizza per verificare se due oggetti sono identici (anziché uguali). Il valore effettivo da cui viene restituito non id()viene praticamente mai utilizzato per nulla perché non ha realmente un significato ed è dipendente dalla piattaforma.


2

È l'indirizzo dell'oggetto in memoria, esattamente come dice il documento. Tuttavia, ha dei metadati allegati, le proprietà dell'oggetto e la posizione nella memoria sono necessarie per memorizzare i metadati. Quindi, quando crei la tua variabile chiamata elenco, crei anche metadati per l'elenco e i suoi elementi.

Quindi, a meno che tu non sia un guru assoluto nella lingua, non puoi determinare l'id dell'elemento successivo della tua lista in base all'elemento precedente, perché non sai cosa assegna la lingua insieme agli elementi.


1
In realtà, essere un guru di Python non servirà a molto prevedere gli oggetti id(). Dovresti avere familiarità con i gestori di memoria (plurale!) In questione, conoscere il loro stato esatto a un certo punto nel tempo e conoscere l'ordine esatto in cui gli oggetti sono stati allocati. In altre parole: non succederà.

Sei un assoluto guru di Python se conosci assolutamente tutte le sue sfumature, inclusi i possibili gestori di memoria.
Lajos Arpad

1
Conoscere Python e conoscere una particolare implementazione (ad esempio CPython) sono due cose completamente diverse. E anche conoscere CPython dentro e fuori non aiuterà, poiché ci sono diversi gestori di memoria che CPython chiama che non fanno parte di CPython ma parte del rispettivo sistema operativo. E come ho detto prima, anche sapere tutte queste cose non Python non ti aiuterà, poiché gli indirizzi effettivi dipendono dallo stato di runtime dei gestori di memoria e talvolta sono anche casuali.

1
Per non parlare del fatto che sarebbe inutile saperlo, poiché fare affidamento su una cosa del genere significherebbe affidarsi a un dettaglio di implementazione e quindi essere fragile e inflessibile.
Gareth Latty

2

Ho un'idea per utilizzare il valore di id()nella registrazione.
È economico da ottenere ed è piuttosto breve.
Nel mio caso utilizzo tornado e id()vorrei avere un'ancora per raggruppare i messaggi sparsi e mescolati su file dal socket web.


0

A partire da python 3 id è assegnato a un valore non a una variabile. Ciò significa che se crei due funzioni come di seguito, tutti e tre gli ID sono uguali.

>>> def xyz():
...     q=123
...     print(id(q))
...
>>> def iop():
...     w=123
...     print(id(w))
>>> xyz()
1650376736
>>> iop()
1650376736
>>> id(123)
1650376736

Anche se questo contraddice le spiegazioni precedenti, sembra accadere e non so perché. Alcuna spiegazione? Ad esempio x [0] = "abc" x [1] = "def" q = ["abc", "def"]. Id (x) == Id (q) è vero. Coincidenza?
VMMF

0

Sono un po 'in ritardo e parlerò di Python3. Per capire cos'è id () e come funziona (e Python), considera il prossimo esempio:

>>> 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

Devi pensare a tutto ciò che è sul lato destro come oggetti. Ogni volta che esegui un compito, crei un nuovo oggetto e questo significa un nuovo ID. Al centro puoi vedere un oggetto "jolly" creato solo per la funzione - id (1000). Quindi, la durata è solo per quella riga di codice. Se controlli la riga successiva, vedrai che quando creiamo una nuova variabile x, ha lo stesso id dell'oggetto jolly. Praticamente funziona come un indirizzo di memoria.


0

Fai attenzione (riguardo alla risposta appena sotto) ... È vero solo perché 123 è compreso tra -5 e 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
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.