आधार वर्ग की एक संपत्ति को कैसे कॉल किया जाए यदि यह संपत्ति व्युत्पन्न वर्ग में अधिलेखित की जा रही है?


87

मैं अपने कुछ वर्गों को गेटर्स के व्यापक उपयोग से बदल रहा हूं और गुणों के अधिक पायथोनिक उपयोग के लिए बसता हूं।

लेकिन अब मैं फंस गया हूं क्योंकि मेरे पिछले कुछ गेटर्स या सेटर बेस क्लास के संबंधित तरीके को कॉल करेंगे, और फिर कुछ और करेंगे। लेकिन यह गुणों के साथ कैसे पूरा किया जा सकता है? पैरेंट क्लास में प्रॉपर्टी गेट्टर या सेटर को कैसे कॉल करें?

बेशक सिर्फ विशेषता को बुलाना ही अनंत पुनरावृत्ति देता है।

class Foo(object):

    @property
    def bar(self):
        return 5

    @bar.setter
    def bar(self, a):
        print a

class FooBar(Foo):

    @property
    def bar(self):
        # return the same value
        # as in the base class
        return self.bar # --> recursion!

    @bar.setter
    def bar(self, c):
        # perform the same action
        # as in the base class
        self.bar = c    # --> recursion!
        # then do something else
        print 'something else'

fb = FooBar()
fb.bar = 7

जवाबों:


100

आप सोच सकते हैं कि आप बेस क्लास फ़ंक्शन को कॉल कर सकते हैं जिसे संपत्ति कहा जाता है:

class FooBar(Foo):

    @property
    def bar(self):
        # return the same value
        # as in the base class
        return Foo.bar(self)

हालाँकि, मुझे लगता है कि यह कोशिश करने के लिए सबसे स्पष्ट बात है - यह काम नहीं करता है क्योंकि बार एक संपत्ति है, कॉल करने योग्य नहीं है।

लेकिन एक गुण सिर्फ एक वस्तु है, इसी विशेषता को खोजने के लिए एक गटर विधि के साथ:

class FooBar(Foo):

    @property
    def bar(self):
        # return the same value
        # as in the base class
        return Foo.bar.fget(self)

मुझे Foo.bar.fset(self, c)विरासत में दिए गए सेटर में क्यों बुलाना चाहिए ? क्यों नहीं Foo.bar.fset(c)- "स्व" के बिना -मुझे लगा कि यह स्पष्ट रूप से पारित हो गया है?
nerdoc

2
मुझे
टाइप टाइप

@nerdoc selfचेन से कहाँ मिलता है Foo.bar.fset?
तदह मैकडॉनल्ड-जेन्सन

बस एक विचार - AFAIK स्वयं हमेशा अंतर्निहित रूप से पारित हो जाता है। यदि आप करते हैं एक foo = Foo()\ foo.bar(c)वहाँ कोई है स्वयं को पारित कर दिया है, लेकिन बार () यह अजगर से प्राप्त करता है। मैं कोई पायथन विशेषज्ञ नहीं हूँ, कमोबेश एक शुरुआत भी। यह सिर्फ एक सोच है।
nerdoc

selfकेवल तभी उत्तीर्ण किया जाता है जब विधि को किसी वर्ग के उदाहरण पर बुलाया जाता है। उदाहरण के लिए, यदि मेरे पास एक Aविधि के साथ एक वर्ग है b(self, arg), और मैं एक उदाहरण बनाता हूं c = A(), तो कॉलिंग c.b(arg)के बराबर हैA.b(c, arg)
टालचौक

53

सुपर को करना चाहिए ट्रिक:

return super().bar

पायथन 2.x में आपको अधिक क्रिया सिंटैक्स का उपयोग करने की आवश्यकता है:

return super(FooBar, self).bar

1
TypeError: सुपर () कम से कम 1 तर्क (0 दिया गया) लेता है
हारून मेंपा

4
मुझे लगता है (ठीक है, लिंक से देखते हुए: पी), यह जवाब python3- संबंधित है। Python3 सुपर में () शून्य तर्क ले सकते हैं, हाँ।
शीलेंट

6
सुपर ()। बार, गेट्टर के लिए ठीक काम करता है, लेकिन ओवरराइड किए गए सेटर में आधार प्रॉपर्टी के जरिए असाइनमेंट के लिए काम नहीं करता है। जब मैं सुपर () बार करता हूं। 3 = मुझे एट्रीब्यूट मिलता है: 'सुपर' ऑब्जेक्ट में कोई विशेषता नहीं है 'बार'
रोब स्मालशायर

1
अच्छी बात है रोब, यह नहीं पता था। यहाँ अधिक जानकारी है: stackoverflow.com/questions/10810369/…
पँकरात

2
जबकि यह प्राप्त करने के लिए काम करता है, यह एक के साथ स्थापित करने में विफल रहता है AttributeError
एथन फुरमैन 20

24

एक वैकल्पिक विकल्प है जिसका उपयोग superआधार वर्ग के नाम को स्पष्ट रूप से संदर्भित करने की आवश्यकता नहीं है।

आधार वर्ग A:

class A(object):
    def __init__(self):
        self._prop = None

    @property
    def prop(self):
        return self._prop

    @prop.setter
    def prop(self, value):
        self._prop = value

class B(A):
    # we want to extend prop here
    pass

B में, मूल वर्ग A के गुणक को एक्सेस करना:

जैसा कि अन्य पहले ही उत्तर दे चुके हैं, यह है:

super(B, self).prop

या अजगर 3 में:

super().prop

यह संपत्ति के गेटर द्वारा लौटाए गए मान को लौटाता है, न कि खुद को पाने वाले को लेकिन यह पर्याप्त है कि वह गेटर को बढ़ाए।

B में, मूल वर्ग A के गुण सेटर तक पहुँचना:

मैंने अब तक देखी सबसे अच्छी सिफारिश निम्नलिखित है:

A.prop.fset(self, value)

मेरा मानना ​​है कि यह बेहतर है:

super(B, self.__class__).prop.fset(self, value)

इस उदाहरण में दोनों विकल्प समतुल्य हैं लेकिन सुपर का उपयोग करने का आधार वर्गों से स्वतंत्र होने का लाभ है B। यदि आप संपत्ति का विस्तार करने वाले वर्ग से भी Bवारिस थे C, तो आपको Bकोड को अपडेट नहीं करना होगा ।

A की संपत्ति का विस्तार करने वाला B का पूरा कोड:

class B(A):
    @property
    def prop(self):
        value = super(B, self).prop
        # do something with / modify value here
        return value

    @prop.setter
    def prop(self, value):
        # do something with / modify value here
        super(B, self.__class__).prop.fset(self, value)

एक चेतावनी:

जब तक आपकी संपत्ति में सेटर नहीं होता है, तब तक आपको सेटर और गेट्टर दोनों को परिभाषित करना होगा, Bभले ही आप उनमें से किसी एक के व्यवहार को बदल दें।


नमस्कार, यह बहुत मददगार है। मेरा यह एक अनुवर्ती प्रश्न है। यह सेटअप अच्छी तरह से काम करता है; हालाँकि, यह तब काम करना बंद कर देता है जब मैं अतिरिक्त गुणों को परिभाषित करने के लिए बी के लिए init सेट करता हूं । क्या B के लिए एक अलग init होने का कोई तरीका है ? धन्यवाद
Sang

क्या आप बता सकते हैं कि कैसे super(B, self.__class__)काम करता है super(class, class)? यह कहाँ प्रलेखित है?
आर्ट

मैं इस सवाल का जवाब करने के लिए कुछ छोटे-मोटे सुधार का सुझाव दिया है मेरा उत्तर
एरिक

4

प्रयत्न

@property
def bar:
    return super(FooBar, self).bar

हालांकि मुझे यकीन नहीं है कि अजगर बेस क्लास प्रॉपर्टी को कॉल करने का समर्थन करता है। एक संपत्ति वास्तव में एक कॉल करने योग्य वस्तु है जिसे निर्दिष्ट फ़ंक्शन के साथ सेट किया गया है और फिर उस नाम को कक्षा में बदल देता है। यह आसानी से मतलब हो सकता है कि कोई सुपर फ़ंक्शन उपलब्ध नहीं है।

आप हमेशा संपत्ति () फ़ंक्शन का उपयोग करने के लिए अपने सिंटैक्स को स्विच कर सकते हैं:

class Foo(object):

    def _getbar(self):
        return 5

    def _setbar(self, a):
        print a

    bar = property(_getbar, _setbar)

class FooBar(Foo):

    def _getbar(self):
        # return the same value
        # as in the base class
        return super(FooBar, self)._getbar()

    def bar(self, c):
        super(FooBar, self)._setbar(c)
        print "Something else"

    bar = property(_getbar, _setbar)

fb = FooBar()
fb.bar = 7

बेस क्लास लिखने पर यह ठीक काम करता है। लेकिन अगर आप तीसरे पक्ष के आधार वर्ग का विस्तार करते हैं जो संपत्ति और गेट्टर के लिए एक ही नाम का उपयोग करता है?
एकैहोला

1

मैक्सिम के जवाब में कुछ छोटे सुधार :

  • __class__लेखन से बचने के लिए उपयोग करना B। ध्यान दें कि self.__class__रनटाइम का प्रकार है self, लेकिन __class__ बिना self एनक्लोजिंग वर्ग की परिभाषा का नाम है। super()के लिए एक आशुलिपि है super(__class__, self)
  • के __set__बजाय का उपयोग कर fset। उत्तरार्द्ध एस के लिए विशिष्ट है property, लेकिन पूर्व सभी संपत्ति जैसी वस्तुओं (विवरणकर्ता) पर लागू होता है।
class B(A):
    @property
    def prop(self):
        value = super().prop
        # do something with / modify value here
        return value

    @prop.setter
    def prop(self, value):
        # do something with / modify value here
        super(__class__, self.__class__).prop.__set__(self, value)

0

आप निम्नलिखित टेम्पलेट का उपयोग कर सकते हैं:

class Parent():
    def __init__(self, value):
        self.__prop1 = value

    #getter
    @property
    def prop1(self):
        return self.__prop1

    #setter
    @prop1.setter
    def prop1(self, value):
        self.__prop1 = value

    #deleter
    @prop1.deleter
    def prop1(self):
        del self.__prop1
  
class Child(Parent):

    #getter
    @property
    def prop1(self):
        return super(Child, Child).prop1.__get__(self)

    #setter
    @prop1.setter
    def prop1(self, value):
        super(Child, Child).prop1.__set__(self, value)

    #deleter
    @prop1.deleter
    def prop1(self):
        super(Child, Child).prop1.__delete__(self)

ध्यान दें! संपत्ति के सभी तरीकों को एक साथ पुनर्परिभाषित किया जाना चाहिए। यदि सभी तरीकों को फिर से परिभाषित नहीं करना चाहते हैं, तो इसके बजाय निम्नलिखित टेम्पलेट का उपयोग करें:

class Parent():
    def __init__(self, value):
        self.__prop1 = value

    #getter
    @property
    def prop1(self):
        return self.__prop1

    #setter
    @prop1.setter
    def prop1(self, value):
        self.__prop1 = value

    #deleter
    @prop1.deleter
    def prop1(self):
        del self.__prop1


class Child(Parent):

    #getter
    @Parent.prop1.getter
    def prop1(self):
        return super(Child, Child).prop1.__get__(self)

    #setter
    @Parent.prop1.setter
    def prop1(self, value):
        super(Child, Child).prop1.__set__(self, value)

    #deleter
    @Parent.prop1.deleter
    def prop1(self):
        super(Child, Child).prop1.__delete__(self)

-4
    class Base(object):
      def method(self):
        print "Base method was called"

    class Derived(Base):
      def method(self):
        super(Derived,self).method()
        print "Derived method was called"

    d = Derived()
    d.method()

(जब तक कि मैं आपके स्पष्टीकरण से कुछ याद नहीं कर रहा हूँ)


1
आप हैं: वह गुणों के बारे में बात कर रहे हैं, न कि सादे तरीकों से
akaihola
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.