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
:
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.
Ok, ma perché non è presente anche in cpython, come documentato? –
ha adeguato il titolo di conseguenza. Grazie! – user228137
@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. –