2014-05-12 8 views

risposta

17

Quello che si suggerisce dovrebbe essere il caso di un dettaglio di implementazione di CPython.

La funzione id():

Ritorna la “identità” di un oggetto. Questo è un numero intero che è garantito essere unico e costante per questo oggetto durante la sua vita.

Dettaglio implementazione CPython: Questo è l'indirizzo dell'oggetto in memoria.

Anche se potrebbero essere equivalenti in CPython, questo non è garantito per le altre implementazioni di Python.


Perché sono valori diversi, anche in CPython?

Si noti che un c_int:

  • è un Python oggetto. CPython id() restituirà l'indirizzo di questo.

  • contiene un valore C di 4 byte int compatibile. ctypes.addressof() restituirà l'indirizzo di questo.

I metadati in un oggetto Python occupano spazio. Per questo motivo, il valore a 4 byte probabilmente non vivrà proprio all'inizio dell'oggetto Python.

Guardate questo esempio:

>>> import ctypes 
>>> i = ctypes.c_int(4) 
>>> hex(id(i)) 
'0x22940d0' 
>>> hex(ctypes.addressof(i)) 
'0x22940f8' 

vediamo che il risultato addressof è solo 0x28 byte superiore al risultato di id(). Giocando con questo un paio di volte, possiamo vedere che questo è sempre il caso. Pertanto, direi che ci sono 0x28 byte di metadati dell'oggetto Python che precedono il valore effettivo int nell'intero c_int.

Nel mio precedente esempio:

c_int 
___________ 
|   | 0x22940d0 This is what id() returns 
| metadata | 
|   | 
|   | 
|   | 
|   | 
|___________| 
| value | 0x22940f8 This is what addressof() returns 
|___________| 

Edit:

Nell'attuazione CPython di ctypes, (origine 2.7.6) Base CDataObject ha un elemento b_ptr che punta il blocco di memoria utilizzato per i dati C dell'oggetto:

union value { 
       char c[16]; 
       short s; 
       int i; 
       long l; 
       float f; 
       double d; 
#ifdef HAVE_LONG_LONG 
       PY_LONG_LONG ll; 
#endif 
       long double D; 
}; 

struct tagCDataObject { 
    PyObject_HEAD 
    char *b_ptr;    /* pointer to memory block */ 
    int b_needsfree;   /* need _we_ free the memory? */ 
    CDataObject *b_base;  /* pointer to base object or NULL */ 
    Py_ssize_t b_size;   /* size of memory block in bytes */ 
    Py_ssize_t b_length;  /* number of references we need */ 
    Py_ssize_t b_index;   /* index of this object into base's 
            b_object list */ 
    PyObject *b_objects;  /* dictionary of references we need 
            to keep, or Py_None */ 
    union value b_value; 
}; 

addressof restituisce questo puntatore come un intero Python:

static PyObject * 
addressof(PyObject *self, PyObject *obj) 
{ 
    if (CDataObject_Check(obj)) 
     return PyLong_FromVoidPtr(((CDataObject *)obj)->b_ptr); 
    PyErr_SetString(PyExc_TypeError, 
        "invalid type"); 
    return NULL; 
} 

Piccolo C oggetti utilizzano il difetto di 16 byte b_value membro della CDataObject. Come mostra l'esempio sopra, questo buffer predefinito viene utilizzato per l'istanza c_int(4). Siamo in grado di girare su se stessa per ctypes introspezione c_int(4) in un processo a 32 bit:

>>> i = c_int(4) 
>>> ci = CDataObject.from_address(id(i)) 

>>> ci 
ob_base: 
    ob_refcnt: 1 
    ob_type: py_object(<class 'ctypes.c_long'>) 
b_ptr: 3071814328 
b_needsfree: 1 
b_base: LP_CDataObject(<NULL>) 
b_size: 4 
b_length: 0 
b_index: 0 
b_objects: py_object(<NULL>) 
b_value: 
    c: b'\x04' 
    s: 4 
    i: 4 
    l: 4 
    f: 5.605193857299268e-45 
    d: 2e-323 
    ll: 4 
    D: 0.0 

>>> addressof(i) 
3071814328 
>>> id(i) + CDataObject.b_value.offset 
3071814328 

Questo trucco sfrutta il fatto che id nel CPython restituisce l'indirizzo di base di un oggetto.

+2

Ok, ma perché non è presente anche in cpython, come documentato? –

+0

ha adeguato il titolo di conseguenza. Grazie! – user228137

+1

@NiklasB. Aggiornato la mia risposta. È perché 'addrsesof' restituisce l'indirizzo dell'attuale' int' all'interno dell'oggetto 'c_int', mentre' id() '(su CPython) restituisce l'indirizzo dell'oggetto stesso python. Sembra esserci un offset 0x28 fisso nella mia implementazione. –