2009-09-08 15 views
83

Diciamo che ho un id di un oggetto Python, che ho recuperato facendo id(thing). Come faccio a trovare thing di nuovo dal numero ID che mi è stato dato?Ottieni oggetto con id()?

+0

Sono curioso: perché vuoi farlo? Qual è il tuo obiettivo? –

+1

@Craig McQueen: http: // stackoverflow.it/questions/1400295/python-class-for-pickle-and-copy-persistent-object –

+0

Non ho potuto ritrovare la fonte di questo, ma ho pensato che ciò che viene restituito da id() è qualunque cosa la particolare distribuzione vuole deve essere. CPython ripristinato potrebbe, al momento, restituire un indirizzo simile alla memoria, ma altre distribuzioni potrebbero restituire diversi tipi di oggetto o interi che non sono puntatori di memoria. Sarebbe bello se esistesse una funzione incorporata per ottenere un oggetto da ciò che viene restituito da id(). Sebbene altri casi d'uso di persistenza siano difficili da immaginare. Anche se * variabile, come C sembra avere senso; Adoro Python per la mancanza di trucchi di punteggiatura come la maggior parte degli altri linguaggi. – DevPlayer

risposta

26

probabilmente si vorrà prendere in considerazione la sua attuazione in un altro modo. Sei a conoscenza del modulo weakref?

(Modificato) Python weakref module consente di mantenere riferimenti, riferimenti di dizionario e proxy agli oggetti senza contare quei riferimenti nel contatore di riferimento. Sono come collegamenti simbolici.

+4

A volte non è possibile creare un riferimento debole all'oggetto, ad esempio: TypeError: impossibile creare un riferimento debole all'oggetto "lxml.etree._Element" – darkk

32

È possibile utilizzare il modulo gc per ottenere tutti gli oggetti attualmente monitorati dal garbage collector Python.

import gc 

def objects_by_id(id_): 
    for obj in gc.get_objects(): 
     if id(obj) == id_: 
      return obj 
    raise Exception("No found") 
+5

Questo ha un problema di aliasing: un ID ottenuto in un punto arbitrario nel passato può ora fare riferimento a un oggetto diverso. – chaos

+6

Fintanto che hai mantenuto un riferimento all'oggetto, ciò non accadrà. Allo stesso modo, questa è generalmente una cattiva idea. –

+0

Sono d'accordo con molti degli altri commentatori: non farlo. Crea il tuo dizionario di oggetti. –

34

Risposta breve, non è possibile.

Risposta lunga, è possibile mantenere un dict per gli ID di mappatura agli oggetti, o cercare l'ID da ricerca esaustiva di gc.get_objects(), ma questo creerà uno dei due problemi: o di riferimento del dict manterrà l'oggetto vivo e prevenire GC oppure (se si tratta di un dict WeakValue o si utilizza gc.get_objects()) l'ID può essere deallocato e riutilizzato per un oggetto completamente diverso.

In sostanza, se si sta tentando di farlo, probabilmente è necessario fare qualcosa di diverso.

+0

Ho intenzione di gettare via il riferimento di identificazione nell'oggetto '__del__', pensi che questo assicurerà che le cose non si rompano? –

+6

+1: Accetto: non farlo. Crea semplicemente un dizionario appropriato di oggetti con le chiavi appropriate: sarai molto più felice. –

+1

@ cool-RR: Sì, dovresti essere al sicuro con quello. – chaos

2

biblioteca eGenix mxTools fornisce una tale funzione, anche se contrassegnato come "esperto-only": mx.Tools.makeref(id)

7

Basta menzionare questo modulo per completezza. This code by Bill Bumgarner include un'estensione C per fare ciò che vuoi senza il loop su ogni oggetto esistente.

Il codice per la funzione è abbastanza semplice. Ogni oggetto Python è rappresentato in C da un puntatore a a PyObject struct. Poiché id(x) è solo l'indirizzo di memoria di questa struttura, possiamo recuperare l'oggetto Python trattando x come puntatore a PyObject, quindi chiamando Py_INCREF per dire al garbage collector che stiamo creando un nuovo riferimento all'oggetto.

static PyObject * 
di_di(PyObject *self, PyObject *args) 
{ 
    PyObject *obj; 
    if (!PyArg_ParseTuple(args, "l:di", &obj)) 
     return NULL; 

    Py_INCREF(obj); 
    return obj; 
} 

Se l'oggetto originale non esiste più, il risultato non è definito. Potrebbe bloccarsi, ma potrebbe anche restituire un riferimento a un nuovo oggetto che ha preso la posizione di quello vecchio in memoria.

116

Se l'oggetto è ancora lì, questo può essere fatto da ctypes:

import ctypes 
a = "hello world" 
print ctypes.cast(id(a), ctypes.py_object).value 

uscita:

hello world 

Se non si sa se l'oggetto è ancora lì, questo è un ricetta per comportamento indefinito e crash strani o peggio, quindi state attenti.

+9

In CPython, oggi, comunque. : ^) – DSM

+4

Questa è la risposta perfetta! –

+8

@HamidFzM No, non proprio. Se ho un ID, forse non so nemmeno se l'oggetto esiste ancora o no. – glglgl

0

Questo farà:

a = 0 
id_a = id(a) 
variables = {**locals(), **globals()} 
for var in variables: 
    exec('var_id=id(%s)'%var) 
    if var_id == id_a: 
     exec('the_variable=%s'%var) 
print(the_variable) 
print(id(the_variable)) 

ma vi suggerisco l'attuazione di un modo più decente.