2013-08-13 3 views
15

मैं यह जांचने में सक्षम होना चाहता हूं कि दो कॉल करने योग्य वस्तुएं समान हैं या नहीं। मैं पहचान अर्थशास्त्र ("है" ऑपरेटर का उपयोग करके) पसंद करूंगा, लेकिन मुझे पता चला है कि जब विधियां शामिल होती हैं, तो कुछ अलग होता है।समानता या पहचान के लिए कार्यों का परीक्षण कैसे किया जाना चाहिए?

#(1) identity and equality with a method 
class Foo(object): 
    def bar(self): 
     pass 
foo = Foo() 
b = foo.bar 
b == foo.bar #evaluates True. why? 
b is foo.bar #evaluates False. why? 

मैं दोनों अजगर 2.7 और 3.3 (CPython) के साथ इस reproduced किया है यकीन है कि यह पुराने संस्करण के एक कार्यान्वयन विस्तार नहीं है बनाने के लिए। अन्य मामलों में, पहचान परीक्षण की उम्मीद के रूप में काम (दुभाषिया सत्र से जारी):

#(2) with a non-method function 
def fun(self): 
    pass 
f = fun 
f == fun #evaluates True 
f is fun #evaluates True 

#(3) when fun is bound as a method 
Foo.met = fun 
foo.met == fun #evaluates False 
foo.met is fun #evaluates False 

#(4) with a callable data member 
class CanCall(object): 
    def __call__(self): 
     pass 
Foo.can = CanCall() 
c = foo.can 
c == foo.can #evaluates True 
c is foo.can #evaluates True 

सवाल How does Python distinguish callback function which is a member of a class? के अनुसार, एक समारोह में जब एक विधि के रूप बाध्य लपेटा जाता है। यह समझ में आता है और उपरोक्त मामले (3) के साथ संगत है।

क्या किसी अन्य नाम के लिए एक विधि को बांधने का कोई भरोसेमंद तरीका है और बाद में उन्हें कॉल करने योग्य ऑब्जेक्ट या सादे फ़ंक्शन के बराबर तुलना करना चाहिए? यदि "==" चाल करता है, तो यह कैसे काम करता है? उपरोक्त (1) मामले में "==" और "है" अलग तरीके से क्यों व्यवहार करते हैं?

संपादित

@Claudiu के रूप में बताया, Why don't methods have reference equality? का जवाब भी इस सवाल का जवाब है।

+0

क्या आपने इसे देखा है? http://stackoverflow.com/questions/306313/python-is-operator-behaves-unexpectedly-with-integers मुझे लगता है कि यह समझने में थोड़ा सा मदद कर सकता है कि यह क्यों हो रहा है। – taronish4

+0

दिलचस्प प्रश्न, पोस्टिंग के लिए धन्यवाद। (+1) – NPE

+0

हाँ संक्षेप में, ऐसा इसलिए है क्योंकि 'है' को कॉल करते समय यह आईडी के लिए जांचता है और मूल्य नहीं है और दो instancianted ऑब्जेक्ट्स में एक ही आईडी नहीं है जबकि '==' की तुलना में यह मान का मूल्य जांचता है वस्तु और न केवल एक त्वरित आईडी जांच। – Torxed

उत्तर

9

अजगर एक विहित नहीं रखता है foo.bar वर्ग foo वर्ग Foo के प्रत्येक उदाहरण के लिए ऑब्जेक्ट। इसके बजाय, पाइथन foo.bar का मूल्यांकन करते समय एक विधि ऑब्जेक्ट बनाया जाता है। इस प्रकार,

foo.bar is not foo.bar 

== के लिए, चीजें गन्दा हो जाती हैं। अजगर, विधि वस्तु प्रकार की एक आश्चर्यजनक रूप से बड़ी संख्या है कि क्या विधि पायथन में कई तरीके तरीकों सी में लागू किया जा सकता से एक लागू किया गया था या आधार पर ये विधि वस्तु प्रकार == अलग ढंग से करने के लिए जवाब:

  • methods written in Python के लिए , == विधियों की तुलना '__func__ और __self__ विशेषताओं की तुलना करता है, यदि विधि ऑब्जेक्ट एक ही कार्य द्वारा लागू विधियों का प्रतिनिधित्व करता है और उसी ऑब्जेक्ट की बजाय बराबर ऑब्जेक्ट्स तक बाध्य करता है तो सत्य लौटाता है। इस प्रकार, x.foo == y.foo सत्य होगा यदि x == y और foo पायथन में लिखा गया है।
  • most "special" methods (__eq__, __repr__, etc.), if they're implemented in C के लिए, पायथन __self__ की तुलना करता है और एक आंतरिक चीज __func__ के समान होती है, फिर सही तरीके से लौटती है यदि विधियों का एक ही कार्यान्वयन होता है और समान वस्तुओं के लिए बाध्य होता है।
  • other methods implemented in C के लिए, पायथन जो वास्तव में आप चाहते हैं वह करता है, यदि सही वस्तुएं उसी ऑब्जेक्ट की एक ही विधि का प्रतिनिधित्व करती हैं तो सही लौटती है।

इस प्रकार, यदि आप निम्न कोड चलाएँ:

class Foo(object): 
    def __eq__(self, other): 
     return True if isinstance(other, Foo) else NotImplemented 
    def foo(self): 
     pass 

print Foo().foo == Foo().foo 
print [].__repr__ == [].__repr__ 
print [].append == [].append 

आप the following bizarre output मिलती है:

True 
True 
False 

आप सबसे अधिक संभावना पहचान अर्थ विज्ञान नहीं करना चाहते, क्योंकि कई वस्तुओं कर सकते हैं एक ही विधि का प्रतिनिधित्व करते हैं। आपको तरीकों के लिए सादे == पर भी भरोसा नहीं करना चाहिए, क्योंकि अर्थशास्त्र बहुत गन्दा हैं, आमतौर पर बेकार है, और अगर सी में विधि को फिर से लिखा जाता है तो बदलने के लिए प्रवण होता है। सौभाग्य से, सभी विधि ऑब्जेक्ट प्रकार जिनके साथ आप सौदा कर सकते हैं __self__ वस्तु जो करने के लिए वे बाध्य कर रहे हैं का प्रतिनिधित्व करने का श्रेय है, तो

meth1.__self__ is meth2.__self__ and meth1 == meth2 

अगर दो विधि वस्तुओं एक ही वस्तु की एक ही विधि का प्रतिनिधित्व करते हैं एक सामान्य तरीके से तुलना करने के लिए किया जाना चाहिए।

1

मैं अपने सभी प्रश्नों के लिए जवाब नहीं है, वहीं मुझे लगता है चाल callables यह है कि (यानी तरीकों के लिए) के लिए __func__ उपयोग करने के लिए है:

In [32]: def same_func(func1, func2): 
    ....:  if hasattr(func1, '__func__'): 
    ....:   func1 = func1.__func__ 
    ....:  if hasattr(func2, '__func__'): 
    ....:   func2 = func2.__func__ 
    ....:  return func1 is func2 
    ....: 

In [33]: same_func(b, foo.bar) 
Out[33]: True 

In [34]: same_func(f, fun) 
Out[34]: True 

In [35]: same_func(foo.met, fun) 
Out[35]: True 

In [36]: same_func(c, foo.can) 
Out[36]: True 
+0

आपको 'im_func' के बजाय' __func__' का उपयोग करना चाहिए क्योंकि python3 में 'im_ * 'विशेषताएँ जहां बाध्य विधियों से हटाया गया है। – Bakuriu

+0

@ बाकुरी: फिक्स्ड, धन्यवाद! – NPE

2

tldr: तरीके वर्णनकर्ता हैं, यही कारण है कि ऐसा हो सकता है। यदि आपको वास्तव में समानता की तुलना करने की आवश्यकता है तो == का उपयोग करें।

is (असल में) id की समानता के लिए परीक्षण। तो चलो कि बाहर की जाँच करते हैं:

>>> id(foo.bar) 
4294145364L 
>>> id(foo.bar) 
4294145364L 
>>> id(foo.bar) 
4294145364L 
>>> b = foo.bar 
>>> id(foo.bar) 
4293744796L 
>>> id(foo.bar) 
4293744796L 
>>> b() 
>>> id(foo.bar) 
4293744796L 
>>> b = 1 
>>> id(foo.bar) 
4294145364L 
>>> type(foo.bar) 
<type 'instancemethod'> 
>>> 

तो, तत्काल कारण है कि अभिव्यक्ति foo.bar रुक-रुक कर एक अलग ऑब्जेक्ट है।

यदि आपको समानता की जांच करने की आवश्यकता है, तो बस == का उपयोग करें। हालांकि, हम सभी इसके निचले हिस्से तक पहुंचना चाहते हैं।

>>> foo.__dict__['bar'] 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
KeyError: 'bar' 
>>> Foo.__dict__['bar'] 
<function bar at 0xffe2233c> 
>>> getattr(foo, 'bar') 
<bound method Foo.bar of <__main__.Foo object at 0xffe2f9ac>> 
>>> foo.bar 
<bound method Foo.bar of <__main__.Foo object at 0xffe2f9ac>> 
>>> 

ऐसा लगता है कि बाध्य तरीकों के बारे में कुछ खास है।

>>> type(foo.bar) 
<type 'instancemethod'> 
>>> help(type(foo.bar)) 
Help on class instancemethod in module __builtin__: 

class instancemethod(object) 
| instancemethod(function, instance, class) 
| 
| Create an instance method object. 
| 
| Methods defined here: 
| 
| __call__(...) 
|  x.__call__(...) <==> x(...) 
| 
| __cmp__(...) 
|  x.__cmp__(y) <==> cmp(x,y) 
| 
| __delattr__(...) 
|  x.__delattr__('name') <==> del x.name 
| 
| __get__(...) 
|  descr.__get__(obj[, type]) -> value 
| 
| __getattribute__(...) 
|  x.__getattribute__('name') <==> x.name 
| 
| __hash__(...) 
|  x.__hash__() <==> hash(x) 
| 
| __repr__(...) 
|  x.__repr__() <==> repr(x) 
| 
| __setattr__(...) 
|  x.__setattr__('name', value) <==> x.name = value 
| 
| ---------------------------------------------------------------------- 
| Data descriptors defined here: 
| 
| __func__ 
|  the function (or other callable) implementing a method 
| 
| __self__ 
|  the instance to which a method is bound; None for unbound methods 
| 
| im_class 
|  the class associated with a method 
| 
| im_func 
|  the function (or other callable) implementing a method 
| 
| im_self 
|  the instance to which a method is bound; None for unbound methods 
| 
| ---------------------------------------------------------------------- 
| Data and other attributes defined here: 
| 
| __new__ = <built-in method __new__ of type object> 
|  T.__new__(S, ...) -> a new object with type S, a subtype of T 

अब, नोटिस इस सूचीबद्ध करता है एक __get__ विधि। इसका मतलब है कि instancemethod ऑब्जेक्ट एक वर्णनकर्ता है। http://docs.python.org/2/reference/datamodel.html#implementing-descriptors के अनुसार अभिव्यक्ति foo.bar(getattr(foo,'bar').__get__(foo) का परिणाम देता है। और यही कारण है कि यह मान बदल सकता है।

क्यों परिवर्तन करता है, मैं आपको नहीं बता सकता, सिवाय इसके कि यह एक कार्यान्वयन विस्तार की संभावना है।

1

आप foo is bar का उपयोग कर सकते हैं जो पहचान को जांचने के लिए id(foo) == id(bar) है। यदि आप 'समानता' (मान) == का उपयोग करना चाहते हैं।

संबंधित मुद्दे