2013-04-12 12 views
10

Ho riscontrato un bug in cui mi sono basato su metodi uguali tra loro quando si utilizza is. Si scopre che non è il caso:Perché i metodi non hanno uguaglianza di riferimento?

>>> class What(object): 
    def meth(self): 
     pass 

>>> What.meth is What.meth 
False 
>>> inst = What() 
>>> inst.meth is inst.meth 
False 

Perché è così? Si lavora per funzioni regolari: oggetti

>>> def func(): 
    pass 

>>> func is func 
True 
+3

Questo funziona in Python 3 btw. – poke

risposta

19

metodo si creano ogni volta che li accedere. Funzioni agiscono come descriptors, restituendo un oggetto metodo quando il metodo viene chiamato .__get__:

>>> What.__dict__['meth'] 
<function meth at 0x10a6f9c80> 
>>> What.__dict__['meth'].__get__(None, What) 
<unbound method What.meth> 
>>> What.__dict__['meth'].__get__(What(), What) 
<bound method What.meth of <__main__.What object at 0x10a6f7b10>> 

Uso == test di uguaglianza invece.

Due metodi sono uguali se gli attributi .im_self e .im_func sono identici. Se è necessario verificare che i metodi rappresentano la stessa sottostante funzione, testare le loro im_func attributi:

>>> What.meth == What.meth  # unbound methods (or functions in Python 3) 
True 
>>> What().meth == What.meth # unbound method and bound method 
False 
>>> What().meth == What().meth # bound methods with *different* instances 
False 
>>> What().meth.im_func == What().meth.im_func # functions 
True 
+0

Se uso '==' otterrò il comportamento di uguaglianza dell'identità che stavo cercando? – Claudiu

+0

Penso che l'uguaglianza dei metodi controlli l'identità per '.im_self', non l'uguaglianza. [check it out] (http://pastebin.com/mSbDWUna) – Claudiu

+0

@Claudiu: sì, scusa, verifica se 'im_self' è identico. –

1

Martijn è giusto che un nuovo metodi sono oggetti generati da .__get__ così i loro puntatori di indirizzo non equiparano con una Valutazione is. Si noti che l'utilizzo di == valuterà come previsto in Python 2.7.

Python2.7 
class Test(object): 
    def tmethod(self): 
     pass 

>>> Test.meth is Test.meth 
False 
>>> Test.meth == Test.meth 
True 

>>> t = Test() 
>>> t.meth is t.meth 
False 
>>> t.meth == t.meth 
True 

nota tuttavia che i metodi di riferimento da un'istanza non equivale a quelli richiamati dalla classe causa del riferimento sé trascinato con il metodo da un'istanza.

>>> t = Test() 
>>> t.meth is Test.meth 
False 
>>> t.meth == Test.meth 
False 

In Python 3.3 l'operatore is per i metodi più spesso si comporta come il == in modo da ottenere il comportamento previsto invece in questo esempio. Ciò risulta dalla scomparsa di __cmp__ e una rappresentazione di un oggetto metodo più pulito in Python 3; i metodi ora hanno __eq__ e i riferimenti non sono oggetti incorporati, quindi il comportamento segue come ci si potrebbe aspettare senza le aspettative di Python 2.

Python3.3 
>>> Test.meth is Test.meth 
True 
>>> Test.meth == Test.meth 
True 
>>> Test.meth.__eq__(Test.meth) 
True 
+0

L'analisi è disattivata. Le modifiche che hai osservato in Python 3 non sono correlate a 'is'; sono dovute alla scomparsa di oggetti metodo non associati in Python 3. 'Test.meth' è ora solo l'oggetto funzione raw che hai definito, invece di un oggetto metodo non creato creato al volo. – user2357112

+0

Ahh sì, ho aggiunto alcuni dettagli chiarificatori per includere il problema relativo all'oggetto non associato. – Pyrce