2010-04-17 10 views
83

मैं हाल ही में पाइथन के साथ खेल रहा हूं, और एक चीज जो मुझे थोड़ा अजीब लग रही है वह 'जादू विधियों' का व्यापक उपयोग है, उदाहरण के लिए इसकी लंबाई उपलब्ध कराने के लिए ऑब्जेक्ट def __len__(self) विधि लागू करता है और फिर जब आप len(obj) लिखते हैं तो इसे कॉल किया जाता है।पाइथन 'जादू विधियों' का उपयोग क्यों करता है?

मैं बस सोच रहा था कि ऑब्जेक्ट्स केवल len(self) विधि को परिभाषित क्यों नहीं करते हैं और इसे सीधे ऑब्जेक्ट के सदस्य के रूप में बुलाया जाता है, उदा। obj.len()? मुझे यकीन है कि पाइथन इसे जिस तरह से कर रहा है, उसके लिए अच्छे कारण होने चाहिए, लेकिन एक नौसिखिया के रूप में मैंने अभी तक काम नहीं किया है।

+4

मुझे लगता है कि सामान्य कारण एक है) ऐतिहासिक और ख) 'तरह LEN (कुछ)' या 'उलट()' वस्तुओं के कई प्रकार के लिए लागू होता है , लेकिन 'एपेंड()' जैसी विधि केवल अनुक्रमों पर लागू होती है, आदि –

उत्तर

55

AFAIK, len इस संबंध में विशेष है और इसकी ऐतिहासिक जड़ें हैं।

यहाँ एक उद्धरण from the FAQ है:

Why does Python use methods for some functionality (e.g. list.index()) but functions for other (e.g. len(list))?

The major reason is history. Functions were used for those operations that were generic for a group of types and which were intended to work even for objects that didn’t have methods at all (e.g. tuples). It is also convenient to have a function that can readily be applied to an amorphous collection of objects when you use the functional features of Python (map(), apply() et al).

In fact, implementing len(), max(), min() as a built-in function is actually less code than implementing them as methods for each type. One can quibble about individual cases but it’s a part of Python, and it’s too late to make such fundamental changes now. The functions have to remain to avoid massive code breakage.

अन्य "जादुई तरीकों" (वास्तव में अजगर लोककथाओं में विशेष विधि कहा जाता है) भावना के बहुत सारे हैं, और इसी तरह की सुविधा अन्य भाषाओं में मौजूद है। वे अधिकतर कोड के लिए उपयोग किए जाते हैं जिन्हें विशेष वाक्यविन्यास का उपयोग करने पर स्पष्ट रूप से बुलाया जाता है।

उदाहरण के लिए: metaprogramming के लिए तक पहुँचने विशेषताओं के लिए

  • अतिभारित ऑपरेटरों (सी में मौजूद ++ और अन्य)
  • निर्माता/नाशक
  • हुक
  • उपकरण

और इतने पर ...

+2

ओह प्रिय। अब मैं एक प्रश्न पूछने के लिए थोड़े मूर्ख महसूस करता हूं जिसका उत्तर एफएक्यू में दिया गया है। मुझे लगता है कि मुझे जाना चाहिए और बाकी को पढ़ना चाहिए, इसलिए मैं वहां कुछ और नहीं पूछता हूं जिसका उत्तर दिया गया है: -एस –

+12

@ ग्रेग: बड़ी संख्या में अपवॉट्स और "पसंदीदा" सितारे दर्शाते हैं कि बहुत से लोगों को यह दिलचस्प लगता है, इसलिए मैं ऐसा नहीं लगता कि आपको इसके बारे में गूंगा महसूस करना है :-) –

+1

[पायथन और कम विस्फोट के सिद्धांत] (http://lucumr.pocoo.org/2011/7/9/python-and-pola/) एक है इस तरह से पाइथन के कुछ फायदों के लिए अच्छा पढ़ा जाता है (हालांकि मैं स्वीकार करता हूं कि अंग्रेजी की जरूरत है)। मूल बिंदु: यह मानक लाइब्रेरी को कोड के एक टन को लागू करने की अनुमति देता है जो बहुत ही पुन: प्रयोज्य हो जाता है लेकिन फिर भी अतिसंवेदनशील होता है। – jpmc26

19

अजगर की जेन से:

In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.

यह एक कारण है - कस्टम तरीकों के साथ, डेवलपर्स getLength(), length(), getlength() या जो भी तरह एक भिन्न तरीके से नाम चुनने के लिए नि: शुल्क किया जाएगा। पायथन सख्त नामकरण को लागू करता है ताकि सामान्य कार्य len() का उपयोग किया जा सके।

कई प्रकार के ऑब्जेक्ट्स के लिए आम तौर पर सभी ऑपरेशन जादू विधियों में डाल दिए जाते हैं, जैसे __nonzero__, __len__ या __repr__। हालांकि, वे ज्यादातर वैकल्पिक हैं।

ऑपरेटर ओवरलोडिंग भी जादू पद्धति (उदा __le__) के साथ किया जाता है, तो यह भी अन्य सामान्य कार्यों के लिए उन्हें इस्तेमाल करने के समझ में आता है।

+0

यह एक आकर्षक तर्क है। अधिक संतोषजनक है कि "Guido वास्तव में ओओ में विश्वास नहीं था" .... (जैसा कि मैंने कहीं और दावा किया है)। –

4

वे वास्तव में "जादू नाम" नहीं हैं। यह केवल एक इंटरफेस है जिसे किसी ऑब्जेक्ट को दी गई सेवा प्रदान करने के लिए लागू करना है। इस अर्थ में, वे किसी पूर्वनिर्धारित इंटरफ़ेस परिभाषा की तुलना में अधिक जादू नहीं हैं जिन्हें आपको पुन: कार्यान्वित करना है।

7

इनमें से कुछ कार्य एक से अधिक विधि को कार्यान्वित करने में सक्षम होंगे (सुपरक्लास पर अमूर्त विधियों के बिना)। उदाहरण के लिए bool() में कार्य करता है एक तरह से इस तरह:

def bool(obj): 
    if hasattr(obj, '__nonzero__'): 
     return bool(obj.__nonzero__()) 
    elif hasattr(obj, '__len__'): 
     if obj.__len__(): 
      return True 
     else: 
      return False 
    return True 

तुम भी 100% यकीन है कि bool() हमेशा सही है या गलत वापस आ जाएगी हो सकता है; यदि आप किसी विधि पर भरोसा करते हैं तो आप पूरी तरह से सुनिश्चित नहीं हो सकते कि आप क्या प्राप्त करेंगे।

कुछ अन्य कार्यों है कि अपेक्षाकृत जटिल कार्यान्वयन (और अधिक जटिल अंतर्निहित जादू तरीकों की तुलना में होने की संभावना है) iter() और cmp(), और सभी विशेषता विधियों (getattr, setattr और delattr) कर रहे हैं। int जैसी चीजें जबरदस्ती करते समय जादू विधियों तक पहुंचती हैं (आप __int__ लागू कर सकते हैं), लेकिन प्रकार के रूप में डबल ड्यूटी करें। len(obj) वास्तव में एक ऐसा मामला है जहां मुझे विश्वास नहीं है कि यह obj.__len__() से अलग है।

+2

'हैटट्रर()' के बजाय मैं 'प्रयास: '/' का उपयोग करता हूं विशेषता एआरआईआर को छोड़कर:' और 'ओबीजे .__ लेन __() के बदले: वापस लौटें: झूठी वापसी' मैं बस 'वापसी obj .__ len __ ()> 0' लेकिन वे सिर्फ स्टाइलिस्ट चीजें हैं। –

+0

पायथन 2.6 (जो बीटीडब्ल्यू 'बूल (एक्स)' 'x .__ nonzero __()') को संदर्भित किया गया है, आपकी विधि काम नहीं करेगी। बूल उदाहरणों में एक विधि '__nonzero __() 'है, और आपका कोड एक बार ओब्ज एक बार था जब आपका कोड कॉलिंग जारी रहेगा। शायद 'बूल (obj .__ बूल __())' उसी तरह से व्यवहार किया जाना चाहिए जिस तरह से आपने '__len__' का इलाज किया था? (या यह कोड वास्तव में पाइथन 3 के लिए काम करता है?) – Ponkadoodle

+0

परिभाषा की विशिष्ट परिपत्र प्रकृति को प्रतिबिंबित करने के लिए, बूल() की गोलाकार प्रकृति कुछ जानबूझकर बेतुका थी। एक तर्क है कि इसे केवल एक आदिम माना जाना चाहिए। –

0

उपरोक्त दो पदों में जोड़ने के लिए बहुत कुछ नहीं है, लेकिन सभी "जादू" फ़ंक्शन वास्तव में जादू नहीं हैं। वे __ buildins__ मॉड्यूल का हिस्सा हैं जो दुभाषिया शुरू होने पर पूरी तरह से/स्वचालित रूप से आयात किया जाता है। आईई:

from __builtins__ import * 

आपके प्रोग्राम शुरू होने से पहले हर बार होता है।

मैंने हमेशा सोचा कि यह अधिक सही होगा अगर पाइथन ने इंटरैक्टिव शैल के लिए ऐसा किया है, और आवश्यक स्क्रिप्ट्स को आवश्यक बिल्डरों से विभिन्न हिस्सों को आयात करने के लिए आवश्यक है। इसके अलावा शायद अलग __ मुख्य__ हैंडलिंग शैल बनाम इंटरैक्टिव में अच्छा होगा। वैसे भी, सभी कार्यों की जाँच, और देखते हैं कि यह उनके बिना की तरह है:

dir (__builtins__) 
... 
del __builtins__ 
1

जबकि कारण ज्यादातर ऐतिहासिक है, वहाँ पायथन के len में कुछ विशेषताओं कि बजाय एक समारोह के उपयोग के लिए एक विधि उचित की कर रहे हैं ।

अजगर में कुछ संचालन,, विधियों के रूप में लागू किया जाता है, उदाहरण के list.index और dict.append के लिए, जबकि दूसरों, callables और जादू तरीके के रूप में लागू किया जाता है, उदाहरण के str और iter और reversed के लिए। दोनों समूह पर्याप्त भिन्न हैं इसलिए विभिन्न दृष्टिकोण उचित हैं:

  1. वे आम हैं।
  2. str, int और दोस्त प्रकार हैं। यह कन्स्ट्रक्टर को कॉल करने के लिए और अधिक समझ में आता है।
  3. कार्यान्वयन फ़ंक्शन कॉल से अलग है। उदाहरण के लिए, iter__getitem__ पर कॉल कर सकता है यदि __iter__ उपलब्ध नहीं है, और अतिरिक्त तर्क का समर्थन करता है जो विधि कॉल में फिट नहीं होते हैं। उसी कारण से it.next() को पायथन के हाल के संस्करणों में next(it) में बदल दिया गया है - यह अधिक समझ में आता है।
  4. इनमें से कुछ ऑपरेटर के करीबी रिश्तेदार हैं। __iter__ और __next__ पर कॉल करने के लिए वाक्यविन्यास है - इसे for लूप कहा जाता है। स्थिरता के लिए, एक समारोह बेहतर है। और यह कुछ अनुकूलन के लिए बेहतर बनाता है।
  5. कुछ फ़ंक्शन बाकी तरीके से बाकी के समान ही हैं - reprstr जैसे कार्य करता है। str(x) बनाम x.repr() भ्रमित हो जाएगा।
  6. उनमें से कुछ शायद ही कभी वास्तविक कार्यान्वयन विधि का उपयोग करते हैं, उदाहरण के लिए isinstance
  7. उनमें से कुछ वास्तविक ऑपरेटर हैं, getattr(x, 'a')x.a और getattr करने का एक और तरीका उपर्युक्त गुणों में से कई साझा करता है।

मैं व्यक्तिगत रूप से पहली समूह विधि-जैसे और दूसरा समूह ऑपरेटर-जैसे कॉल करता हूं। यह बहुत अच्छा भेद नहीं है, लेकिन मुझे उम्मीद है कि यह किसी भी तरह से मदद करता है।

यह कहकर, len दूसरे समूह में बिल्कुल फिट नहीं है। यह पहले के संचालन के करीब है, केवल अंतर के साथ कि यह लगभग किसी भी तरह से अधिक आम है। लेकिन यह एकमात्र चीज है जो __len__ पर कॉल कर रही है, और यह L.index के बहुत करीब है। हालांकि, कुछ मतभेद हैं। उदाहरण के लिए, __len__ को bool जैसी अन्य सुविधाओं के कार्यान्वयन के लिए बुलाया जा सकता है, यदि विधि len कहलाती है तो आप कस्टम len विधि के साथ bool(x) तोड़ सकते हैं जो पूरी तरह से अलग चीज करता है।

संक्षेप में, आपके पास ऑब्जेक्ट निर्माण के दौरान, एक विशेष फ़ंक्शन (आमतौर पर ऑपरेटर के रूप में कार्यान्वयन से अधिक करता है) के माध्यम से ऑपरेटर के माध्यम से एक्सेस किया जा सकता है, जो बहुत ही सामान्य विशेषताओं का एक सेट है, और वे सभी कुछ सामान्य लक्षण साझा करते हैं। बाकी सब एक विधि है। और len उस नियम के अपवाद का कुछ हद तक है।

13

पायथन शब्द का उपयोग करता है: - "जादू तरीके" क्योंकि, वे विधियां वास्तव में आपके प्रोग्राम के लिए जादू करती हैं। पायथन के जादू तरीकों का उपयोग करने के सबसे बड़े फायदों में से एक यह है कि वे वस्तुओं को अंतर्निहित प्रकारों के समान व्यवहार करने का एक आसान तरीका प्रदान करते हैं। इसका मतलब है कि आप बुनियादी ऑपरेटरों को निष्पादित करने के बदसूरत, काउंटर-सहज और गैर-मानक तरीकों से बच सकते हैं। ,

dict1 = {1 : "ABC"} 
dict2 = {2 : "EFG"} 

dict1 + dict2 
Traceback (most recent call last): 
    File "python", line 1, in <module> 
TypeError: unsupported operand type(s) for +: 'dict' and 'dict' 

यह एक त्रुटि देता है क्योंकि शब्दकोश प्रकार इसके समर्थन नहीं करता है -:

एक निम्न उदाहरण पर विचार करें। अब, चलो वर्ग शब्दकोश का विस्तार करने और जोड़ने "__add__" जादू विधि: -

class AddableDict(dict): 

    def __add__(self, otherObj): 
     self.update(otherObj) 
     return AddableDict(self) 


dict1 = AddableDict({1 : "ABC"}) 
dict2 = AddableDict({2 : "EFG"}) 

print (dict1 + dict2) 

अब, यह उत्पादन निम्नलिखित देता है,

{1: 'ABC', 2: 'EFG'} 

इस प्रकार, इस विधि जोड़कर, अचानक जादू हुआ है और जो त्रुटि आप पहले प्राप्त कर रहे थे, चली गई है।

मुझे उम्मीद है कि यह आपको चीजों को स्पष्ट करता है। अधिक जानकारी के लिए नीचे दिए गए लिंक को देखें: -

http://web.archive.org/web/20161024123835/http://www.rafekettler.com/magicmethods.html

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