Oggetto del metodo associato e non associato in Python

Ho provato un po ‘di codice sui metodi legati e non associati. Quando li chiamiamo, penso che entrambi restituirebbero degli oggetti. Ma quando uso id() per ottenere alcune informazioni, restituisce qualcosa che non capisco.

IDE: Eclipse

Plugin: pydev

 Class C(object): def foo(self): pass cobj = C() print id(C.foo) #1 print id(cobj.foo) #2 a = C.foo b = cobj.foo print id(a) #3 print id(b) #4 

E l’output è …

 5671672 5671672 5671672 5669368 

Perché i numeri 1 e 2 restituiscono lo stesso ID? Non sono oggetti diversi? E se assegniamo C.foo e conj.foo a due variabili, # 3 e # 4 restituiscono l’id differente.

Penso che i numeri 3 e 4 dimostrino che non sono lo stesso object, ma i numeri 1 e 2 …

Qual è la differenza tra il metodo id of bound e un metodo non associato?

Ogni volta che si ricerca un metodo tramite instance.name (e in Python 2, class.name ), l’object metodo viene creato a-new. Python usa il protocollo descrittore per avvolgere la funzione in un object metodo ogni volta.

Quindi, quando si cerca id(C.foo) , viene creato un nuovo object metodo, si recupera il suo id (un indirizzo di memoria), quindi si elimina nuovamente l’object metodo . Quindi si cerca id(cobj.foo) , un nuovo object metodo creato che riutilizza l’indirizzo di memoria ora liberato e si vede lo stesso valore. Il metodo viene quindi, di nuovo, scartato (garbage collection quando il conteggio dei riferimenti scende a 0).

Successivamente, è stato memorizzato un riferimento al metodo non C.foo in una variabile. Ora l’indirizzo di memoria non viene liberato (il numero di riferimento è 1, invece di 0) e si crea una seconda istanza di metodo cercando su cobj.foo che deve utilizzare una nuova posizione di memoria. Quindi ottieni due valori diversi.

Vedi la documentazione per id() :

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

Dettaglio implementazione CPython : questo è l’indirizzo dell’object in memoria.

Enfasi mia.

Puoi ricreare un metodo usando un riferimento diretto alla funzione tramite l’attributo __dict__ della class, quindi chiamando il metodo __get__ descriptor :

 >>> class C(object): ... def foo(self): ... pass ... >>> C.foo  >>> C.__dict__['foo']  >>> C.__dict__['foo'].__get__(None, C)  >>> C.__dict__['foo'].__get__(C(), C) > 

Si noti che in Python 3, l’intera distinzione metodo non associato / vincolato è stata eliminata; ottieni una funzione in cui prima dovresti ottenere un metodo non associato e un metodo altrimenti, dove un metodo è sempre associato:

 >>> C.foo  >>> C.foo.__get__(None, C)  >>> C.foo.__get__(C(), C) > 

Inoltre, Python 3.7 aggiunge una nuova coppia di opcode LOAD_METHODCALL_METHOD che sostituisce la coppia LOAD_ATTRIBUTECALL_FUNCTION opcode in modo preciso per evitare ogni volta la creazione di un nuovo object metodo. Questa ottimizzazione trasforma il percorso executon per instance.foo() da type(instance).__dict__['foo'].__get__(instance, type(instance))() con type(instance).__dict__['foo'](instance) , quindi passa “manualmente” all’istanza direttamente all’object funzione.

Aggiungendo all’ottima risposta di @Martijn Pieters:

 In [1]: class C(object): ...: def foo(self): ...: pass ...: In [2]: c = C() In [3]: id(c.foo), id(C.foo) Out[3]: (149751844, 149751844) # so 149751844 is current free memory address In [4]: a = c.foo # now 149751844 is assigned to a In [5]: id(a) Out[5]: 149751844 # now python will allocate some different address to c.foo and C.foo In [6]: id(c.foo), id(C.foo) # different address used this time, and Out[6]: (149752284, 149752284) # that address is freed after this step # now 149752284 is again free, as it was not allocated to any variable In [7]: b = C.foo # now 149752284 is allocated to b In [8]: id(b) Out[8]: 149752284 In [9]: c.foo is C.foo # better use `is` to compare objects, rather than id() Out[9]: False