पायथन 3.x का सुपर () जादू क्यों है?


159

पायथन 3.x में, super()बिना बहस के बुलाया जा सकता है:

class A(object):
    def x(self):
         print("Hey now")

class B(A):
    def x(self):
        super().x()
>>> B().x()
Hey now

इस काम को करने के लिए, कुछ संकलन-समय का जादू किया जाता है, जिसका एक परिणाम यह है कि निम्नलिखित कोड (जो रिबंड superकरता है super_) विफल रहता है:

super_ = super

class A(object):
    def x(self):
        print("No flipping")

class B(A):
    def x(self):
        super_().x()
>>> B().x()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in x
RuntimeError: super(): __class__ cell not found

super()संकलक की सहायता के बिना रनटाइम पर सुपरक्लास को हल करने में असमर्थ क्यों है ? क्या व्यावहारिक परिस्थितियां हैं जिनमें यह व्यवहार, या इसके लिए अंतर्निहित कारण, एक अवांछित प्रोग्रामर को काट सकता है?

... और, एक पक्ष के प्रश्न के रूप में: क्या फ़ंक्शंस, विधियों आदि के पायथन में कोई अन्य उदाहरण हैं, जो उन्हें एक अलग नाम से विद्रोह करके तोड़ा जा सकता है?


6
मैं इस एक पर अरमान को समझाने देता हूँ । यह भी एक और अच्छी पोस्ट है
गेम्स ब्रेनियाक

संबंधित: stackoverflow.com/q/36993577/674039
wim

जवाबों:


217

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

class Foo(Bar):
    def baz(self):
        return super(Foo, self).baz() + 42

Spam = Foo
Foo = something_else()

Spam().baz()  # liable to blow up

यही बात क्लास डेकोरेटर्स का उपयोग करने पर लागू होती है जहाँ डेकोरेटर एक नई वस्तु देता है, जो क्लास के नाम को रीबाइंड करता है:

@class_decorator_returning_new_class
class Foo(Bar):
    def baz(self):
        # Now `Foo` is a *different class*
        return super(Foo, self).baz() + 42

मैजिक super() __class__सेल आपको मूल वर्ग ऑब्जेक्ट तक पहुंच प्रदान करके इन मुद्दों को अच्छी तरह से दरकिनार कर देता है।

पीईपी को गुइडो द्वारा मार दिया गया था, जिसने शुरू में superएक कीवर्ड बनने की कल्पना की थी , और वर्तमान वर्ग को देखने के लिए एक सेल का उपयोग करने का विचार भी उसका था । निश्चित रूप से, इसे एक कीवर्ड बनाने का विचार पीईपी के पहले मसौदे का हिस्सा था ।

हालाँकि, यह वास्तव में स्वयं Guido था, जिसने इसके बाद वर्तमान कार्यान्वयन का प्रस्ताव करते हुए कीवर्ड आइडिया को 'बहुत जादुई' के रूप में छोड़ दिया। उन्होंने अनुमान लगाया कि एक अलग नाम का उपयोग करने से super()समस्या हो सकती है :

मेरा पैच एक मध्यवर्ती समाधान का उपयोग करता है: यह मानता है कि __class__ जब भी आप एक चर नाम का उपयोग करते हैं, तो आपको इसकी आवश्यकता होती है 'super'। इस प्रकार, यदि आप (विश्व स्तर पर) का नाम बदलने superके लिए supperऔर उपयोग supperनहीं बल्कि super, यह तर्क बिना काम नहीं करेगा (लेकिन यह अभी भी काम करता है, तो आप इसे पारित या तो __class__या वास्तविक वर्ग वस्तु); यदि आपके पास नाम का एक असंबंधित चर है super, तो चीजें काम करेंगी लेकिन विधि सेल चर के लिए उपयोग किए जाने वाले थोड़ा धीमा कॉल पथ का उपयोग करेगी।

तो, अंत में, यह स्वयं Guido था जिसने घोषणा की कि एक superकीवर्ड का उपयोग करना सही नहीं लगा, और यह कि एक जादू __class__सेल प्रदान करना एक स्वीकार्य समझौता था।

मैं मानता हूं कि कार्यान्वयन का जादू, निहित व्यवहार कुछ आश्चर्यजनक है, लेकिन super()भाषा में सबसे गलत तरीके से लागू कार्यों में से एक है। बस इंटरनेट पर पाए जाने वाले सभी गलत super(type(self), self)या चालान पर एक नज़र डालें super(self.__class__, self); यदि उस कोड को कभी भी एक व्युत्पन्न वर्ग से बुलाया गया था तो आप एक अनंत पुनरावृत्ति अपवाद के साथ समाप्त हो जाएंगे । बहुत कम से कम सरलीकृत super()कॉल, बिना तर्क के, उस समस्या से बचा जाता है

नाम के लिए के रूप में super_; सिर्फ संदर्भ __class__अपने विधि में के रूप में अच्छी तरह से और इसे फिर से काम करेंगे। यदि आप या तो आपके विधि में नाम super या संदर्भ देखें तो सेल बनाया गया है __class__:

>>> super_ = super
>>> class A(object):
...     def x(self):
...         print("No flipping")
... 
>>> class B(A):
...     def x(self):
...         __class__  # just referencing it is enough
...         super_().x()
... 
>>> B().x()
No flipping

1
अच्छा लिखना है। हालांकि यह अभी भी कीचड़ के रूप में स्पष्ट है। आप कह रहे हैं कि सुपर () एक def super(of_class=magic __class__)तरह की तरह स्वचालित रूप से त्वरित फ़ंक्शन के बराबर है self.super(); def super(self): return self.__class__?
चार्ल्स मरिअम

17
@CharlesMerriam: यह पोस्ट super()बिना तर्क के काम करने के बारे में नहीं है ; यह ज्यादातर इस बात से संबंधित है कि यह क्यों मौजूद है। super()एक वर्ग विधि में, के बराबर है super(ReferenceToClassMethodIsBeingDefinedIn, self), जहां ReferenceToClassMethodIsBeingDefinedInसंकलित समय पर निर्धारित किया जाता है, एक बंद नाम के रूप में विधि से जुड़ा हुआ है __class__, और super()रनटाइम पर कॉलिंग फ़्रेम से दोनों को देखेंगे। लेकिन आपको वास्तव में यह सब जानने की जरूरत नहीं है।
मार्टिन पीटर्स

1
@CharlesMerriam: लेकिन super()कहीं भी एक स्वचालित रूप से त्वरित कार्य होने के करीब है , नहीं।
मार्टिन पीटर्स

1
@ chris.leonard: प्रमुख वाक्य यह है कि यदि आप सुपर () का उपयोग करते हैं या __class__अपनी पद्धति का उपयोग करते हैं तो सेल बनाया जाता है । आपने superअपने फ़ंक्शन में नाम का उपयोग किया है । संकलक देखता है और __class__बंद करता है।
मार्टिज़न पीटरर्स

4
@ एसेक्स: यह पर्याप्त नहीं है। वर्तमान प्रकार type(self)देता है , जो कि विधि पर परिभाषित प्रकार के समान नहीं है। तो विधि की जरूरत वाला एक वर्ग , क्योंकि यह उप-वर्ग के रूप में हो सकता है , जिस बिंदु पर है और आपको एक अनंत लूप देगा। मेरे उत्तर में मेरे द्वारा लिंक की गई पोस्ट देखें: जब एक व्युत्पन्न वर्ग में सुपर () को कॉल किया जाता है, तो क्या मैं स्वयं .__ class__ में उत्तीर्ण हो सकता हूं? Foobazsuper(Foo, self).baz()class Ham(Foo):type(self)Hamsuper(type(self), self).baz()
मार्टिन पीटर्स
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.