उदाहरण के स्तर पर एक विधि को ओवरराइड करें


87

क्या पायथन में एक तरीका है उदाहरण के स्तर पर एक वर्ग विधि को ओवरराइड करना? उदाहरण के लिए:

class Dog:
    def bark(self):
        print "WOOF"

boby = Dog()
boby.bark() # WOOF
# METHOD OVERRIDE
boby.bark() # WoOoOoF!!

जवाबों:


11

कृपया ऐसा न करें जैसा दिखाया गया है। जब आप क्लास से अलग होने का उदाहरण देते हैं तो आप कोड अपठनीय हो जाते हैं।

आप बंदरों के कोड को डीबग नहीं कर सकते।

जब आप में एक बग मिल bobyऔर print type(boby), आपको लगता है कि (क) देखेंगे यह एक कुत्ता है, लेकिन (ख) कुछ अस्पष्ट कारण यह छाल को सही ढंग से नहीं करता है के लिए। यह एक बुरा सपना है। ऐसा मत करो।

कृपया इसके बजाय ऐसा करें।

class Dog:
    def bark(self):
        print "WOOF"

class BobyDog( Dog ):
    def bark( self ):
        print "WoOoOoF!!"

otherDog= Dog()
otherDog.bark() # WOOF

boby = BobyDog()
boby.bark() # WoOoOoF!!

9
@arivero: मैंने सोचा कि "कृपया ऐसा न करें जैसा कि दिखाया गया है" जो पूरी तरह से स्पष्ट है। आप इसे और अधिक स्पष्ट करने के लिए कौन से अन्य या अलग-अलग शब्द देखना चाहेंगे कि यह उस प्रश्न का उत्तर नहीं दे रहा है जो पूछा गया था, लेकिन यह एक बुरा विचार है कि क्यों सलाह दे रहा है?
— एस.लॉट

45
मैं सलाह में असहमत नहीं हूं, न ही ओपी जैसा लगता है। लेकिन मैं मानता हूं कि लोगों के पास पूछने के लिए कारण हैं। या यहां तक ​​कि अगर ओपी नहीं है, तो अन्य भविष्य के आगंतुक कर सकते हैं। तो, IMHO, एक उत्तर प्लस एक फटकार बेहतर है कि बस एक फटकार है।
— २१:

13
@arivero: कि मेरे सवाल का जवाब नहीं दिया।
— एस.लॉट

9
@ S.Lott मुझे लगता है कि आपको "दिखाए गए" को वास्तविक उत्तर से जोड़ना चाहिए, मुझे वास्तव में स्वीकार किए गए उत्तर के साथ कोई समस्या नहीं है लेकिन ऐसे कारण हैं जो आपको कुछ परिस्थितियों में और मेरे स्किम पढ़ने से बंदर के पैच की आवश्यकता है मैंने "जैसा दिखाया" का अर्थ लिया कि आप जो दिखा रहे थे, वह एक अलग उत्तर के बजाय।
— डैनियल चैटफील्ड

4
उप-विधि एक बंदर-पैचिंग विधि की तुलना में बहुत मजबूत अनुबंध है। जब संभव हो, मजबूत अनुबंध अप्रत्याशित व्यवहार को रोकते हैं। लेकिन कुछ मामलों में, एक शिथिल अनुबंध वांछनीय है। उन स्थितियों में, मैं एक कॉलबैक का उपयोग करना पसंद करूंगा - बंदर-पेटिंग के बजाय - क्योंकि, कुछ व्यवहार को अनुकूलित करने की अनुमति देकर, वर्ग अनुबंध असंबद्ध रहता है। आपका जवाब, हालांकि व्यावहारिक सलाह, इस प्रश्न के लिए काफी खराब है और आपकी कोडिंग शैली असंगत है।
— आरोन 3468 11

166

हाँ, यह मुमकिन है:

class Dog:
    def bark(self):
        print "Woof"

def new_bark(self):
    print "Woof Woof"

foo = Dog()

funcType = type(Dog.bark)

# "Woof"
foo.bark()

# replace bark with new_bark for this object only
foo.bark = funcType(new_bark, foo, Dog)

foo.bark()
# "Woof Woof"

1
एक छोटी सी टिप्पणी, जब आप funcType (new_bark, foo, Dog) करते हैं, तो उसका नाम == छाल और उदाहरण विधि को जोड़ा जाता है। foo में। Dog.new_bark .__ dict__ सही है? इसलिए, जब आप दोबारा कॉल करते हैं तो यह पहली बार उदाहरण शब्दकोश में दिखता है और उसके लिए कॉल करें,
— जेम्स

11
कृपया बताएं कि यह क्या करता है, विशेष रूप से क्या funcTypeकरता है और क्यों आवश्यक है।
— Aleksandr Dubinsky

3
मुझे लगता है कि इसे funcType = types.MethodType(आयात करने के बाद types) उपयोग करके थोड़ा सरल और अधिक स्पष्ट किया जा सकता है funcType = type(Dog.bark)।
— एलियास ज़मरिया

13
मुझे लगता है कि यह पायथन 3 के साथ काम नहीं करता है। मुझे "टाइपर्रर: फ़ंक्शन () तर्क 1 कोड होना चाहिए, फ़ंक्शन नहीं" त्रुटि। अजगर 3 के लिए कोई सुझाव?
— सैट

2
आप __get__इसे उदाहरण पर बाँधने के लिए फ़ंक्शन पर भी कॉल कर सकते हैं ।
— मैड

37

आपको मॉड्यूल से MethodType का उपयोग करने की आवश्यकता है types। उद्देश्य का उद्देश्य MethodTypeउदाहरण के तरीकों को अधिलेखित करना है (ताकि यह selfअधिलेखित विधियों में उपलब्ध हो सके)।

नीचे दिए गए उदाहरण देखें।

import types

class Dog:
    def bark(self):
        print "WOOF"

boby = Dog()
boby.bark() # WOOF

def _bark(self):
    print "WoOoOoF!!"

boby.bark = types.MethodType(_bark, boby)

boby.bark() # WoOoOoF!!

31

@ कोडेलोगिक के उत्कृष्ट उत्तर की व्याख्या करने के लिए, मैं एक अधिक स्पष्ट दृष्टिकोण का प्रस्ताव करता हूं। यह वही तकनीक है जो .ऑपरेटर पूरी तरह से एक क्लास पद्धति को बांधने के लिए जाता है जब आप इसे एक उदाहरण विशेषता के रूप में एक्सेस करते हैं, सिवाय इसके कि आपका तरीका वास्तव में एक क्लास के बाहर परिभाषित फ़ंक्शन होगा।

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

क्लास डॉग:
    डीप बार्क (स्व):
        "Woof" प्रिंट करें

def new_bark (स्व):
    "Woof Woof" प्रिंट करें

फू = कुत्ता ()

# "वूफ़"
foo.bark ()

# बार्क को केवल इस ऑब्जेक्ट के लिए new_bark से बदलें
foo.bark = new_bark .__ get __ (फू, डॉग)

foo.bark ()
# "वूफ वूफ"

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

एकमात्र कारण यह है कि इस तरह से काम करता है, उदाहरण है कि गैर-डेटा विवरणों को ओवरराइड करता है । डेटा डिस्क्रिप्टर में __set__विधियाँ हैं, जो विधियाँ (सौभाग्य से आपके लिए) नहीं हैं। कक्षा में डेटा डिस्क्रिप्टर वास्तव में किसी भी उदाहरण विशेषताओं पर प्राथमिकता लेते हैं। इसीलिए आप किसी प्रॉपर्टी को असाइन कर सकते हैं: __set__जब आप असाइनमेंट बनाने की कोशिश करते हैं, तो उनका तरीका लागू हो जाता है। मैं व्यक्तिगत रूप से इसे एक कदम आगे ले जाना और उदाहरण में अंतर्निहित विशेषता के वास्तविक मूल्य को छिपाना पसंद करता हूं __dict__, जहां यह सामान्य तरीके से दुर्गम है क्योंकि संपत्ति इसे छाया देती है।

आपको यह भी ध्यान रखना चाहिए कि यह जादू (डबल अंडरस्कोर) तरीकों के लिए व्यर्थ है । जादू के तरीकों को इस तरह से ओवरराइड किया जा सकता है, लेकिन इनका उपयोग करने वाले ऑपरेशन केवल प्रकार को देखते हैं। उदाहरण के लिए, आप __contains__अपने उदाहरण में कुछ विशेष करने के लिए सेट कर सकते हैं , लेकिन कॉलिंग x in instanceकी अवहेलना और type(instance).__contains__(instance, x)इसके बजाय उपयोग करना होगा। यह पायथन डेटा मॉडल में निर्दिष्ट सभी जादू विधियों पर लागू होता है ।


1
यह स्वीकृत उत्तर होना चाहिए: यह साफ है और पाइथन 3 में काम करता है
— ब्लेंडरबेंडर

@BlenderBender। मैं आपके समर्थन की सराहना करता हूं
— मैड फिजिसिस्ट

@ हर्षल धुमई से इस उत्तर में और आपके ऊपर एक के बीच क्या अंतर है?
— 1313

1
@ 1313। कार्यात्मक रूप से, बहुत अंतर नहीं होना चाहिए। मुझे लगता है कि ऊपर दिए गए उत्तर पर संदेह है कि इनपुट के रूप में कॉलबल्स की एक विस्तृत श्रृंखला को स्वीकार किया जा सकता है, लेकिन डॉक्स को पढ़े और इधर-उधर खेले बिना, मुझे यकीन नहीं है।
— मैड फिजिसिस्ट

इसके लिए अजगर 2 डॉक्स: docs.python.org/2/howto/descriptor.html#functions-and-methods । तो सैद्धांतिक रूप से यह @ हर्षल धुमई के उत्तर के समान है।
— रॉकलाइट

26
class Dog:
    def bark(self):
        print "WOOF"

boby = Dog()
boby.bark() # WOOF

# METHOD OVERRIDE
def new_bark():
    print "WoOoOoF!!"
boby.bark = new_bark

boby.bark() # WoOoOoF!!

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


1
मूल हस्ताक्षर का उपयोग करते हुए IMHO पठनीयता में जोड़ता है, खासकर यदि फ़ंक्शन कोड में कहीं और परिभाषित किया गया है, उदाहरण के पास नहीं। अपवाद वह मामला होगा जहां ओवरराइडिंग विधि का उपयोग स्वतंत्र रूप से एक फ़ंक्शन के रूप में भी किया जाता है। बेशक, इस सरल उदाहरण में, यह कोई फर्क नहीं पड़ता।
— कोडेलजिक डिस

1
मुझे समझ नहीं आ रहा है कि यह स्वीकृत उत्तर क्यों नहीं है। इसे कहा जाता है patchingऔर यह इसे करने का सही तरीका है (जैसे boby = Dog()और boby.bark = new_bark)। यह नियंत्रण के लिए इकाई परीक्षण में अविश्वसनीय रूप से उपयोगी है । अधिक स्पष्टीकरण के लिए देखें tryolabs.com/blog/2013/07/05/run-time-method-patching-python (उदाहरण) - नहीं मैं लिंक की गई साइट या लेखक से संबद्ध नहीं हूं।
— ज्योफ

5
new_bark पद्धति में स्वयं (इंस्टेंस) तक पहुंच नहीं है, इसलिए कोई तरीका नहीं है जिससे उपयोगकर्ता new_bark में इंस्टेंस प्रॉपर्टीज तक पहुंच सके। इसके बजाय एक प्रकार मॉड्यूल से MethodType का उपयोग करने की आवश्यकता है (नीचे मेरा उत्तर देखें)।
— हर्षल धूमल

3

चूँकि यहाँ कोई उल्लेख नहीं कर रहा है functools.partial:

from functools import partial

class Dog:
    name = "aaa"
    def bark(self):
        print("WOOF")

boby = Dog()
boby.bark() # WOOF

def _bark(self):
    print("WoOoOoF!!")

boby.bark = partial(_bark, boby)
boby.bark() # WoOoOoF!!

1

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

class Dog:
    def __init__(self,  barkmethod=None):
        self.bark=self.barkp
        if barkmethod:
           self.bark=barkmethod
    def barkp(self):
        print "woof"

d=Dog()
print "calling original bark"
d.bark()

def barknew():
    print "wooOOOoof"

d1=Dog(barknew)
print "calling the new bark"
d1.bark()

def barknew1():
    print "nowoof"

d1.bark=barknew1
print "calling another new"
d1.bark()

और परिणाम हैं

calling original bark
woof
calling the new bark
wooOOOoof
calling another new
nowoof

-4

यद्यपि मुझे एस। लोट से विरासत का विचार पसंद आया और 'प्रकार (ए)' की बात से सहमत हूं, लेकिन चूंकि कार्यों में भी सुलभ गुण हैं, मुझे लगता है कि इसे इस तरह से प्रबंधित किया जा सकता है:

class Dog:
    def __init__(self, barkmethod=None):
        self.bark=self.barkp
        if barkmethod:
           self.bark=barkmethod
    def barkp(self):
        """original bark"""
        print "woof"

d=Dog()
print "calling original bark"
d.bark()
print "that was %s\n" % d.bark.__doc__

def barknew():
    """a new type of bark"""
    print "wooOOOoof"

d1=Dog(barknew)
print "calling the new bark"
d1.bark()
print "that was %s\n" % d1.bark.__doc__

def barknew1():
    """another type of new bark"""
    print "nowoof"

d1.bark=barknew1
print "another new"
d1.bark()
print "that was %s\n" % d1.bark.__doc__

और आउटपुट है:

calling original bark
woof
that was original bark

calling the new bark
wooOOOoof
that was a new type of bark

another new
nowoof
that was another type of new bark

2
अगर इसे "प्रबंधित" करने की आवश्यकता है, तो - मेरे लिए - कुछ गलत है। विशेषकर तब जब प्रथम श्रेणी की भाषा की सुविधा हो जो पहले से ही काम करती है।
— S.Lott

-4

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


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