2009-10-27 6 views
9

मैं एक अजगर नौसिखिया हूं, और मुझे यकीन नहीं है कि क्यों पाइथन लागू किया गया है (obj), max (obj), और न्यूनतम (ओबीजे) obj.len(), obj.max(), और obj.min()विरासत विधि पर लेन(), अधिकतम(), और न्यूनतम() जैसे स्थिर फ़ंक्शन होने के फायदे

पर एक स्थिर जैसे कार्यों (मैं जावा भाषा से हूं) के रूप में फायदे और नुकसान (अन्य के अलावा) स्पष्ट असंगतता) लेन() ... विधि कॉल पर रखने के?

क्यों guido ने विधि कॉल पर इसे चुना? (यदि आवश्यक हो तो इसे पायथन 3 में हल किया जा सकता था, लेकिन इसे पायथन 3 में नहीं बदला गया था, इसलिए अच्छे कारण होना चाहिए ... मुझे उम्मीद है)

धन्यवाद !!

+2

मुझे व्यक्तिगत रूप से 'न्यूनतम (1,2) 'और' मिनट ([1,2])' बेहद संगत होने के लिए मिलता है। सब कुछ जावा नहीं होना चाहिए –

+0

@ टॉमलेज़: 'x.foo()' जैसी विधि कॉल के साथ 'len (x)' संगत कैसे है? वे ऑब्जेक्ट एक्स पर दोनों ऑपरेशन हैं, है ना? यह जावा जैसी होने के बारे में नहीं है। यह [कम आश्चर्य के सिद्धांत] (http://en.wikipedia.org/wiki/Principle_of_least_surprise) के अनुरूप है। रूबी उस सिद्धांत का पालन करता है। –

उत्तर

19

लेन को लागू करने और कॉल करने वालों को हमेशा अधिक पठनीय लेन (obj) तक पहुँच जाने के लिए है बड़ा फायदा यह है कि अंतर्निहित फ़ंक्शंस (और ऑपरेटर) विशेष तरीकों को कॉल करने से परे उपयुक्त होने पर अतिरिक्त तर्क लागू कर सकते हैं। उदाहरण के लिए, min कई तर्क देख सकते हैं और उचित असमानता जांच लागू कर सकते हैं, या यह एक एकल पुनरावृत्ति तर्क स्वीकार कर सकता है और इसी तरह आगे बढ़ सकता है; abs जब किसी विशेष विधि के बिना किसी ऑब्जेक्ट पर कॉल किया जाता है __abs__ 0 के साथ कहा गया ऑब्जेक्ट की तुलना करने और आवश्यकता होने पर ऑब्जेक्ट चेंज साइन विधि का उपयोग करने का प्रयास कर सकता है (हालांकि यह वर्तमान में नहीं है); इत्यादि।

तो, स्थिरता के लिए, व्यापक प्रयोज्यता वाले सभी संचालन हमेशा अंतर्निर्मित और/या ऑपरेटरों के माध्यम से जाना चाहिए, और यह उचित विशेष तरीकों को देखने और लागू करने के लिए अंतर्निहित ज़िम्मेदारी है (एक या अधिक तर्कों पर), जहां लागू लागू वैकल्पिक तर्क का उपयोग करें, और बहुत आगे।

एक उदाहरण जहां यह सिद्धांत सही ढंग से लागू नहीं किया गया था (लेकिन असंगतता पाइथन 3 में तय की गई थी) "2.5 ए और पहले में" है, आपको गैर-विशेष रूप से नामित next को परिभाषित करने और कॉल करने की आवश्यकता है इटरेटर पर विधि। 2.6 और बाद में आप इसे सही तरीके से कर सकते हैं: iterator ऑब्जेक्ट __next__ को परिभाषित करता है, नया next अंतर्निहित इसे और अतिरिक्त तर्क लागू कर सकता है, उदाहरण के लिए डिफ़ॉल्ट मान (2 में।6 आप पिछली संगतता के लिए अभी भी खराब पुराने तरीके से कर सकते हैं, हालांकि 3.* में आप और नहीं कर सकते हैं)।

एक और उदाहरण: x + y अभिव्यक्ति पर विचार करें। एक पारंपरिक ऑब्जेक्ट उन्मुख भाषा में (केवल पायथन, रूबी, जावा, सी ++, सी #, & सी जैसे बाएं तर्क के प्रकार पर प्रेषण करने में सक्षम) यदि x कुछ अंतर्निर्मित प्रकार का है और y आपके स्वयं का है फैंसी नया प्रकार, आप दुर्भाग्य से भाग्यशाली हैं अगर भाषा type(x) की विधि में सभी तर्कों को प्रस्तुत करने पर जोर देती है जो अतिरिक्त लागू करती है (मानते हुए कि भाषा ऑपरेटर ओवरलोडिंग की अनुमति देता है ;-)।

अजगर में, + ऑपरेटर (और इसी तरह के पाठ्यक्रम में निर्मित operator.add, अगर है कि तुम क्या पसंद करते हैं है) की कोशिश करता है एक्स के प्रकार के __add__, और y के प्रकार के __radd__ अगर है कि एक क्या y से कोई लेना देना नहीं जानता है, तो कोशिश करता है। तो आप अपने प्रकारों को परिभाषित कर सकते हैं जो खुद को पूर्णांक, फ्लोट्स, कॉम्प्लेक्स इत्यादि में जोड़ने के बारे में जानते हैं, साथ ही साथ जो जानते हैं कि इन अंतर्निहित संख्यात्मक प्रकारों को स्वयं कैसे जोड़ना है (यानी, आप इसे कोड कर सकते हैं ताकि x + y और y + x दोनों ठीक काम करते हैं, जब y आपके फैंसी नए प्रकार का उदाहरण है और x कुछ अंतर्निहित संख्यात्मक प्रकार का उदाहरण है)।

"सामान्य कार्यों" (शिखर के रूप में) एक और अधिक सुरुचिपूर्ण दृष्टिकोण हैं (वाम-पंथी तर्क है कि OOP को प्रोत्साहित करती है पर पागल एकोंमादी ध्यान देने के साथ किसी भी अधिभावी प्रकारों के संयोजन पर आधारित है, कभी नहीं की इजाजत दी -!), लेकिन (ए) दुर्भाग्यवश उन्हें पायथन 3 के लिए स्वीकार नहीं किया गया था, और (बी) वे निश्चित रूप से जेनेरिक फ़ंक्शन को मुक्त-स्टैंड के रूप में व्यक्त करने की आवश्यकता होती है (यह किसी भी प्रकार के "फ़ंक्शन" को "संबंधित" के रूप में मानना ​​बिल्कुल पागल होगा , जहां पूरा POINT है जिसे कई तर्कों के मनमाना संयोजन के आधार पर अलग-अलग ओवरराइड/अधिभारित किया जा सकता है! -)। कोई भी जो आम लिस्प, डाइलन या पीएक में प्रोग्राम किया गया है, जानता है कि मैं किस बारे में बात कर रहा हूं ;-)।

तो, नि: शुल्क से चली आ रही कार्य करता है और ऑपरेटरों अभी भी एक है, यह है सिर्फ सही, लगातार रास्ते जाने के लिए (भले ही सामान्य कार्यों की कमी, नंगे हड्डियों अजगर में, हटाने निहित लालित्य का कुछ अंश करता हैं लालित्य और व्यावहारिकता का उचित मिश्रण! -)।

+0

+1। – whaley

+1

आप कहते हैं "केवल बाईं ओर तर्क के प्रकार पर प्रेषण करने में सक्षम - जैसे पायथन, सी ++, ..." और फिर दिखाएं कि पायथन '__radd__' के साथ सही तर्क पर भी प्रेषित कर सकता है। साथ ही, यदि आप 'ऑपरेटर +' को एक फ्री फ़ंक्शन के रूप में घोषित करते हैं तो सी ++ दोनों तर्कों पर प्रेषण कर सकता है। यह थोड़ा असंगत प्रतीत होता है ... – sth

+2

@sth, पायथन आरएचएस तर्क पर _dispatch_ नहीं कर सकता है - यह गंदे काम करने के लिए एक बिल्टिन या ऑपरेटर_ का उपयोग कर सकता है। सी ++ में, यदि 'ऑपरेटर +' एक फ्री-स्टैंडिंग फ़ंक्शन है, तो इसे ऑपरेटरों के _statically visual_ (उर्फ संकलन-समय दृश्यमान) प्रकारों के आधार पर अधिभारित किया जा सकता है, यह ** ** ** ** डिस्पैच ** नहीं हो सकता है, यानी, ऑपरेशन के ** रनटाइम ** प्रकारों का सख्ती से उपयोग करने के लिए कौन सा कोड निष्पादित करना है (सदस्य कार्य के रूप में, लेकिन केवल ** बाएं हाथ के किनारे ** प्रकार पर आधारित) का उपयोग करें। पाठकों के लिए कुछ भी असंगत नहीं है, जो जानता है कि प्रेषण का मतलब क्या है, और संकलन-समय और रन-टाइम प्रकारों के बीच का अंतर! -) –

3

यह किसी ऑब्जेक्ट की क्षमताओं पर जोर देता है, न कि इसकी विधियों या प्रकार। कैपेबिलिट्स को "सहायक" कार्यों द्वारा घोषित किया जाता है जैसे __iter__ और __len__ लेकिन वे इंटरफ़ेस नहीं बनाते हैं। इंटरफ़ेस बिल्टिन फ़ंक्शंस में है, और उसके अलावा इंडेक्सिंग और स्लाइसिंग के लिए + और [] जैसे बुलेट-इन ऑपरेटरों में भी है।

कभी-कभी, यह एक-से-एक संवाद नहीं है: उदाहरण के लिए, iter(obj) किसी ऑब्जेक्ट के लिए एक पुनरावर्तक देता है, और __iter__ परिभाषित नहीं होने पर भी काम करेगा। यदि परिभाषित नहीं किया गया है, तो यह देखने के लिए चला जाता है कि ऑब्जेक्ट __getitem__ को परिभाषित करता है और ऑब्जेक्ट इंडेक्स-वार (एक सरणी की तरह) तक पहुंचने वाला एक इटरेटर लौटाएगा।

यह पाइथन के डक टाइपिंग के साथ मिलकर जाता है, हम केवल इस बात की परवाह करते हैं कि हम किसी ऑब्जेक्ट के साथ क्या कर सकते हैं, न कि यह किसी विशेष प्रकार का है।

3

वास्तव में, वे उन तरीकों से "स्थैतिक" विधियां नहीं हैं जिनके बारे में आप सोच रहे हैं। वे built-in functions हैं जो कि वास्तव में उन पायथन वस्तुओं पर कुछ विधियों के लिए उपनाम हैं जो उन्हें लागू करते हैं।

>>> class Foo(object): 
...  def __len__(self): 
...    return 42 
... 
>>> f = Foo() 
>>> len(f) 
42 

ये हमेशा यह कहने के लिए उपलब्ध होते हैं कि ऑब्जेक्ट उन्हें लागू करता है या नहीं। बिंदु कुछ स्थिरता है। इसके बजाय कुछ वर्ग) एक विधि लंबाई (बुलाया और एक अन्य बुलाया आकार() होने का, सम्मेलन obj.methodThatDoesSomethingCommon

के बजाय
+0

नहीं, यह सिर्फ लेन है। कोई '__min__' या' __max__' विशेष विधियां नहीं हैं। –

+1

मुद्दा यह है कि सवाल उन सभी को समान रूप से व्यवहार करता है, जो गलत है। यह जवाब कहता है - सही ढंग से - यह सामान्य पैटर्न नहीं है, लेकिन एक सामान्य नोटेशन के साथ चीजों का मिश्रण है। –

+0

यहां तक ​​कि 'एफ .__ लेन __() '' len (f) 'के लिए काफी उपनाम नहीं है। यदि आपकी '__len__' विशेष विधि कुछ भी पूर्णांक (या यहां तक ​​कि एक बड़ा पूर्णांक) देता है तो 'len' फ़ंक्शन' TypeError' के साथ विफल हो जाएगा। बिल्ट-इन्स के लाभों के लिए एक महान स्पष्टीकरण के लिए –

1

मैंने सोचा था कि कारण यह था कि कंटेनर के समान इंटरफ़ेस वाले इटरेटर पर इन बुनियादी संचालन किए जा सकते थे। हालांकि, यह वास्तव में लेन के साथ काम नहीं करता है:

def foo(): 
    for i in range(10): 
     yield i 
print len(foo()) 

... TypeError के साथ विफल रहता है। लेन() एक पुनरावर्तक का उपभोग और गणना नहीं करेगा; यह केवल उन वस्तुओं के साथ काम करता है जिनमें __len__ कॉल है।

तो, जहां तक ​​मेरा संबंध है, लेन() मौजूद नहीं होना चाहिए। लेन (ओबीजे) की तुलना में obj.len कहने के लिए यह और अधिक स्वाभाविक है, और शेष भाषा और मानक पुस्तकालय के साथ बहुत अधिक संगत है। हम परिशिष्ट नहीं कहते (एलएसटी, 1); हम lst.append (1) कहते हैं। लंबाई के लिए एक अलग वैश्विक विधि होने के नाते एक अजीब, असंगत विशेष मामला है, और वैश्विक नामस्थान में एक बहुत ही स्पष्ट नाम खाता है, जो पाइथन की एक बहुत बुरी आदत है।

यह बतख टाइपिंग से असंबंधित है; आप यह निर्धारित करने के लिए getattr(obj, "len") कह सकते हैं कि आप किसी ऑब्जेक्ट पर आसानी से लेन का उपयोग कर सकते हैं - और अधिक लगातार - getattr(obj, "__len__") का उपयोग कर सकते हैं।

जो कुछ भी कहा जाता है, भाषा वार के रूप में जाना जाता है - जो लोग इसे एक वार्ट पर विचार करते हैं - यह एक बहुत ही आसान व्यक्ति है जिसके साथ रहना है।

दूसरी तरफ, न्यूनतम और अधिकतम पुनरावृत्तियों पर काम करते हैं, जो उन्हें किसी विशेष वस्तु के अलावा उपयोग देता है। यह स्पष्ट है, तो मैं बस एक उदाहरण देता हूँ:

import random 
def foo(): 
    for i in range(10): 
     yield random.randint(0, 100) 
print max(foo()) 

हालांकि, वहाँ अपने व्यवहार को ओवरराइड करने के लिए कोई __min__ या __max__ तरीके हैं, इसलिए वहाँ क्रमबद्ध कंटेनरों के लिए कुशल खोज प्रदान करने के लिए कोई सुसंगत तरीका है। यदि एक कंटेनर को उसी कुंजी पर सॉर्ट किया गया है जिसे आप खोज रहे हैं, तो न्यूनतम (अधिकतम) ओ (एन) के बजाय ओ (1) ऑपरेशंस हैं, और इसका खुलासा करने का एकमात्र तरीका एक अलग, असंगत विधि है। (यह निश्चित रूप से भाषा में अपेक्षाकृत आसानी से तय किया जा सकता है।)

इसके साथ किसी अन्य समस्या का पालन करने के लिए: यह पाइथन की विधि बाध्यकारी के उपयोग को रोकता है। एक सरल, काल्पनिक उदाहरण के रूप में, आप एक सूची के लिए मान जोड़ने के लिए एक समारोह की आपूर्ति करने के लिए इस कर सकते हैं:

def add(f): 
    f(1) 
    f(2) 
    f(3) 
lst = [] 
add(lst.append) 
print lst 

और यह सब सदस्य कार्यों पर काम करता है। आप न्यूनतम, अधिकतम या लेन के साथ ऐसा नहीं कर सकते हैं, हालांकि, वे उस ऑब्जेक्ट के तरीके नहीं हैं जिन पर वे काम करते हैं। इसके बजाए, आपको functools.partial का सहारा लेना होगा, जो कि अन्य भाषाओं में आम तौर पर एक बेकार द्वितीय श्रेणी का वर्कअराउंड है।

बेशक, यह एक असामान्य मामला है; लेकिन यह असामान्य मामलों है जो हमें एक भाषा की स्थिरता के बारे में बताते हैं।

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