पायथन में, मैं कैसे इंगित करता हूं कि मैं एक विधि को ओवरराइड कर रहा हूं?


173

जावा में, उदाहरण के लिए, @Overrideएनोटेशन न केवल ओवरराइड की संकलन-समय की जाँच प्रदान करता है, बल्कि उत्कृष्ट स्व-दस्तावेजीकरण कोड के लिए बनाता है।

मैं सिर्फ प्रलेखन की तलाश कर रहा हूं (हालांकि अगर यह पाइलिंट जैसे कुछ चेकर का संकेतक है, तो यह एक बोनस है)। मैं कहीं टिप्पणी या डॉकस्ट्रिंग जोड़ सकता हूं, लेकिन पायथन में एक ओवरराइड को इंगित करने के लिए मुहावरेदार तरीका क्या है?


13
दूसरे शब्दों में, आप कभी संकेत नहीं करते हैं कि आप एक विधि को ओवरराइड कर रहे हैं? पाठक को यह जानने के लिए छोड़ दें कि क्या वह खुद बाहर है?
ब्लू जू

2
हाँ, मुझे पता है कि यह एक संकलित भाषा से आने वाली त्रुटि की स्थिति की तरह लगता है, लेकिन आपको इसे स्वीकार करना होगा। व्यवहार में मैंने इसे एक समस्या के रूप में नहीं पाया है (मेरे मामले में रूबी, पायथन नहीं, लेकिन एक ही विचार)
एड एस।

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

1
यह सीधे तौर पर एक जैसा नहीं है, लेकिन एब्सट्रैक्ट बेस क्लास चेक करते हैं कि क्या सभी एब्स्ट्रैक्ट मेथड्स को एक उपवर्ग द्वारा ओवरराइड किया गया है। यदि आप ठोस तरीकों से आगे निकल रहे हैं तो बेशक यह मदद नहीं करता है।
लेटमाईक

जवाबों:


208

इसके आधार पर और fwc: s answer मैंने एक पाइप इंस्टाल करने योग्य पैकेज बनाया https://github.com/mkorpela/overrides

समय-समय पर मैं इस सवाल को देख रहा हूं। मुख्य रूप से हमारे कोड बेस में एक ही बग को देखने के बाद (फिर से) ऐसा होता है: किसी ने "इंटरफ़ेस" में एक विधि का नाम बदलने के दौरान कुछ "इंटरफ़ेस" लागू करने वाले वर्ग को भुला दिया है।

खैर पायथन जावा नहीं है, लेकिन पायथन में शक्ति है - और स्पष्ट रूप से निहित से बेहतर है - और वास्तविक दुनिया में वास्तविक ठोस मामले हैं जहां इस चीज ने मेरी मदद की होगी।

इसलिए यहां ओवरराइड डेकोरेटर का एक स्केच है। यह जांच करेगा कि पैरामीटर के रूप में दी गई कक्षा में विधि (या कुछ) नाम उसी विधि के रूप में है जिसे सजाया जा रहा है।

यदि आप एक बेहतर समाधान के बारे में सोच सकते हैं तो कृपया इसे यहाँ पोस्ट करें!

def overrides(interface_class):
    def overrider(method):
        assert(method.__name__ in dir(interface_class))
        return method
    return overrider

यह निम्नानुसार काम करता है:

class MySuperInterface(object):
    def my_method(self):
        print 'hello world!'


class ConcreteImplementer(MySuperInterface):
    @overrides(MySuperInterface)
    def my_method(self):
        print 'hello kitty!'

और यदि आप एक दोषपूर्ण संस्करण करते हैं तो यह क्लास लोडिंग के दौरान एक त्रुटि को बढ़ाएगा:

class ConcreteFaultyImplementer(MySuperInterface):
    @overrides(MySuperInterface)
    def your_method(self):
        print 'bye bye!'

>> AssertionError!!!!!!!

18
बहुत बढ़िया। यह एक गलत वर्तनी बग पकड़ा पहली बार मैं इसे करने की कोशिश की। कुडोस।
क्रिस्टोफर ब्रून्स

7
mfbutner: यह विधि निष्पादित होने पर हर बार नहीं कहा जाता है - केवल जब विधि बनाई जाती है।
मकार्पेला

3
यह डॉक्टर स्ट्रिंग्स के लिए भी अच्छा है! overridesओवरराइडिंग विधि का अपना एक नहीं होने पर ओवरराइड विधि की डॉकस्ट्रिंग कॉपी कर सकता है।
लेटैमिक

5
@mkorpela, हे तुम्हारा यह कोड अजगर डिफ़ॉल्ट कार्य प्रणाली में होना चाहिए। आप इसे पाइप सिस्टम में क्यों नहीं डालते? : पी

5
@mkorpela: ओह और मैं इस पैकेज के बारे में अजगर कोर डेवलपर्स को सूचित करने का सुझाव देता हूं, वे मुख्य अजगर सिस्टम में डेकोरेटर को जोड़ने के बारे में विचार करना चाह सकते हैं। :)

30

यहां एक कार्यान्वयन है जिसे इंटरफ़ेस_क्लास नाम के विनिर्देश की आवश्यकता नहीं है।

import inspect
import re

def overrides(method):
    # actually can't do this because a method is really just a function while inside a class def'n  
    #assert(inspect.ismethod(method))

    stack = inspect.stack()
    base_classes = re.search(r'class.+\((.+)\)\s*\:', stack[2][4][0]).group(1)

    # handle multiple inheritance
    base_classes = [s.strip() for s in base_classes.split(',')]
    if not base_classes:
        raise ValueError('overrides decorator: unable to determine base class') 

    # stack[0]=overrides, stack[1]=inside class def'n, stack[2]=outside class def'n
    derived_class_locals = stack[2][0].f_locals

    # replace each class name in base_classes with the actual class type
    for i, base_class in enumerate(base_classes):

        if '.' not in base_class:
            base_classes[i] = derived_class_locals[base_class]

        else:
            components = base_class.split('.')

            # obj is either a module or a class
            obj = derived_class_locals[components[0]]

            for c in components[1:]:
                assert(inspect.ismodule(obj) or inspect.isclass(obj))
                obj = getattr(obj, c)

            base_classes[i] = obj


    assert( any( hasattr(cls, method.__name__) for cls in base_classes ) )
    return method

2
थोड़ा जादुई लेकिन विशिष्ट उपयोग को बहुत आसान बनाता है। क्या आप उपयोग के उदाहरण शामिल कर सकते हैं?
Bluu

इस डेकोरेटर का उपयोग करने की औसत और सबसे खराब स्थिति क्या है, शायद इसकी तुलना बिल्ड-इन डेकोरेटर जैसे @classmethod या @property से की जाती है?
लार्हम 1

4
@ लार्हम 1 इस डेकोरेटर को एक बार निष्पादित किया जाता है, जब कक्षा की परिभाषा का विश्लेषण किया जाता है, प्रत्येक कॉल पर नहीं। इसलिए यह निष्पादन लागत अप्रासंगिक है, जब प्रोग्राम रनटाइम की तुलना में।
अबगन

यह PEP 487 की बदौलत पायथन 3.6 में बहुत अच्छा होगा ।
नील जी

, '। ओवरराइड विधि "{}" नहीं आधार वर्ग में पाया गया था' (base_classes में cls के लिए hasattr (सीएलएस, विधि .__ name__)) किसी भी ज़ोर प्रारूप (विधि .__ name__): बेहतर त्रुटि संदेश प्राप्त करने के लिए।
इवान Kovtun

14

यदि आप इसे केवल प्रलेखन उद्देश्यों के लिए चाहते हैं, तो आप अपने स्वयं के ओवरराइड डेकोरेटर को परिभाषित कर सकते हैं:

def override(f):
    return f


class MyClass (BaseClass):

    @override
    def method(self):
        pass

यह वास्तव में आंख-कैंडी के अलावा कुछ भी नहीं है, जब तक कि आप इस तरह से ओवरराइड (एफ) नहीं बनाते हैं जो वास्तव में ओवरराइड के लिए जांच करता है।

लेकिन फिर, यह पायथन है, इसे जावा की तरह क्यों लिखा गया?


2
overrideडेकोरेटर के निरीक्षण के माध्यम से वास्तविक सत्यापन को जोड़ा जा सकता है ।
एरिक कपलुन

70
लेकिन फिर, यह पायथन है, इसे जावा की तरह क्यों लिखा गया? क्योंकि जावा में कुछ विचार अच्छे हैं और अन्य भाषाओं के विस्तार के लायक हैं?
पायोत्र डोब्रोगॉस्ट

9
क्योंकि जब आप किसी सुपरक्लास में एक विधि का नाम बदलते हैं, तो यह जानना अच्छा होगा कि कुछ उपवर्ग 2 स्तर नीचे थे जो इसे ओवरराइड कर रहे थे। ज़रूर, यह जांचना आसान है, लेकिन भाषा पार्सर से थोड़ी मदद चोट नहीं पहुंचाएगी।
01 पर Abgan

4
क्योंकि यह एक अच्छा विचार है। तथ्य यह है कि अन्य भाषाओं की एक किस्म की विशेषता है, कोई तर्क नहीं है - या तो या इसके खिलाफ।
sfkleach

6

पायथन जावा नहीं है। वास्तव में संकलन-समय की जाँच जैसी कोई चीज नहीं है।

मुझे लगता है कि डॉकस्ट्रिंग में एक टिप्पणी काफी है। इससे आपकी विधि का कोई भी उपयोगकर्ता टाइप कर सकता है help(obj.method)और देख सकता है कि यह विधि ओवरराइड है।

आप स्पष्ट रूप से एक इंटरफ़ेस का विस्तार कर सकते हैं class Foo(Interface), जो उपयोगकर्ताओं को help(Interface.method)उस कार्यक्षमता के बारे में एक विचार प्राप्त करने के लिए टाइप करने की अनुमति देगा, जिसे आपकी विधि प्रदान करने का इरादा है।


57
@Overrideजावा में वास्तविक बिंदु दस्तावेज़ नहीं है - यह एक गलती पकड़ना है जब आप एक विधि को ओवरराइड करने का इरादा रखते हैं, लेकिन एक नए को परिभाषित करने के लिए समाप्त हो गया (उदाहरण के लिए क्योंकि आपने एक नाम को गलत ठहराया है; जावा में, ऐसा इसलिए भी हो सकता है क्योंकि आपने उपयोग किया था; गलत हस्ताक्षर, लेकिन यह पायथन में कोई समस्या नहीं है - लेकिन वर्तनी की गलती अभी भी है)।
पावेल मिनाएव

2
@ पावेल मिनाव: सच है, लेकिन प्रलेखन के लिए यह अभी भी सुविधाजनक है, खासकर यदि आप एक आईडीई / पाठ संपादक का उपयोग कर रहे हैं जिसमें ओवरराइड्स के लिए स्वचालित संकेतक नहीं हैं (एक्लिप्स जेडडीटी उन्हें लाइन नंबरों के साथ बड़े पैमाने पर दिखाता है, उदाहरण के लिए)।
तुकका मस्टोनन

2
@PavelMinaev गलत। @Overrideसंकलन समय की जाँच के अलावा प्रलेखन का एक मुख्य बिंदु है।
सियामई

6
@siamii मुझे लगता है कि दस्तावेज़ीकरण के लिए एक सहायता महान है, लेकिन सभी आधिकारिक जावा दस्तावेज़ीकरण में मैं देख रहा हूं, वे केवल संकलन के समय के महत्व को इंगित करते हैं। कृपया अपने दावे को पुष्ट करें कि पावेल "गलत है।"
एंड्रयू मेलिंगर

5

@Mkorpela महान उत्तर पर सुधार , यहां एक संस्करण है

अधिक सटीक जाँच, नामकरण, और त्रुटि ऑब्जेक्ट्स को उठाया

def overrides(interface_class):
    """
    Function override annotation.
    Corollary to @abc.abstractmethod where the override is not of an
    abstractmethod.
    Modified from answer https://stackoverflow.com/a/8313042/471376
    """
    def confirm_override(method):
        if method.__name__ not in dir(interface_class):
            raise NotImplementedError('function "%s" is an @override but that'
                                      ' function is not implemented in base'
                                      ' class %s'
                                      % (method.__name__,
                                         interface_class)
                                      )

        def func():
            pass

        attr = getattr(interface_class, method.__name__)
        if type(attr) is not type(func):
            raise NotImplementedError('function "%s" is an @override'
                                      ' but that is implemented as type %s'
                                      ' in base class %s, expected implemented'
                                      ' type %s'
                                      % (method.__name__,
                                         type(attr),
                                         interface_class,
                                         type(func))
                                      )
        return method
    return confirm_override


यह वही है जो व्यवहार में दिखता है:

NotImplementedError" बेस क्लास में लागू नहीं "

class A(object):
    # ERROR: `a` is not a implemented!
    pass

class B(A):
    @overrides(A)
    def a(self):
        pass

अधिक वर्णनात्मक NotImplementedErrorत्रुटि के परिणामस्वरूप

function "a" is an @override but that function is not implemented in base class <class '__main__.A'>

पूर्ण हो चुकी है

Traceback (most recent call last):
  
  File "C:/Users/user1/project.py", line 135, in <module>
    class B(A):
  File "C:/Users/user1/project.py", line 136, in B
    @overrides(A)
  File "C:/Users/user1/project.py", line 110, in confirm_override
    interface_class)
NotImplementedError: function "a" is an @override but that function is not implemented in base class <class '__main__.A'>


NotImplementedError" अपेक्षित कार्यान्वित प्रकार "

class A(object):
    # ERROR: `a` is not a function!
    a = ''

class B(A):
    @overrides(A)
    def a(self):
        pass

अधिक वर्णनात्मक NotImplementedErrorत्रुटि के परिणामस्वरूप

function "a" is an @override but that is implemented as type <class 'str'> in base class <class '__main__.A'>, expected implemented type <class 'function'>

पूर्ण हो चुकी है

Traceback (most recent call last):
  
  File "C:/Users/user1/project.py", line 135, in <module>
    class B(A):
  File "C:/Users/user1/project.py", line 136, in B
    @overrides(A)
  File "C:/Users/user1/project.py", line 125, in confirm_override
    type(func))
NotImplementedError: function "a" is an @override but that is implemented as type <class 'str'> in base class <class '__main__.A'>, expected implemented type <class 'function'>




@Mkorpela उत्तर के बारे में महान बात यह है कि चेक कुछ आरंभीकरण चरण के दौरान होता है। चेक को "चलाने" की आवश्यकता नहीं है। पूर्व उदाहरणों का उल्लेख करते हुए, class Bकभी भी प्रारंभिक ( B()) अभी तक नहीं NotImplementedErrorउठाया जाएगा। इसका मतलब है कि overridesत्रुटियां जल्द पकड़ी जाती हैं।


अरे! यह दिलचस्प लग रहा है। क्या आप मेरी ipromise परियोजना पर एक पुल अनुरोध करने पर विचार कर सकते हैं? मैंने एक उत्तर जोड़ा है।
नील जी

@ नीलू ने आईप्रोमाइस प्रोजेक्ट को कांट-छांट किया और थोड़ा सा कोड किया। ऐसा लगता है कि आपने अनिवार्य रूप से इसे भीतर लागू कर दिया है overrides.py। मुझे यकीन है कि और क्या मैं काफी से अपवाद प्रकार बदलने के लिए छोड़कर सुधार कर सकते हैं नहीं कर रहा हूँ TypeErrorकरने के लिए NotImplementedError
जेम्सटॉमासून १

अरे! धन्यवाद, मेरे पास चेक नहीं है कि ओवरराइड ऑब्जेक्ट वास्तव में टाइप है types.MethodType। आपके उत्तर में यह एक अच्छा विचार था।
नील जी

2

जैसा कि अन्य लोगों ने कहा है कि जावा के विपरीत @ ऑवराइड टैग नहीं है, हालांकि ऊपर आप अपने स्वयं के सज्जाकार बना सकते हैं, हालांकि मैं आंतरिक डिक्टेट का उपयोग करने के बजाय गेटैट्रीब () वैश्विक पद्धति का उपयोग करने का सुझाव दूंगा ताकि आपको निम्न जैसा कुछ मिल जाए:

def Override(superClass):
    def method(func)
        getattr(superClass,method.__name__)
    return method

यदि आप चाहते हैं कि आप अपने स्वयं के प्रयास में अपनी खुद की त्रुटि को पकड़ें (तो) गेटअटर () पकड़ सकते हैं, लेकिन मुझे लगता है कि इस मामले में गेटअटर विधि बेहतर है।

इसके अलावा यह क्लास मेथड और वाइराबल्स सहित क्लास से जुड़ी सभी वस्तुओं को पकड़ता है


2

@ Mkorpela के शानदार उत्तर के आधार पर, मैंने एक समान पैकेज ( ipromise pypi github ) लिखा है जो इसे और अधिक करता है:

मान लीजिए कि Aविरासत में मिली Bऔर C, से Bविरासत मिली C

मॉड्यूल ipromise जाँच करता है कि:

  • यदि A.fओवरराइड होता है B.f, तो B.fअस्तित्व में होना चाहिए, और इससे Aविरासत में प्राप्त होना चाहिए B। (यह ओवरराइड्स पैकेज से चेक है)।

  • आपके पास यह A.fघोषित करने वाला पैटर्न नहीं है कि यह ओवरराइड करता है B.f, जो तब घोषणा करता है कि यह ओवरराइड करता है C.fAकहना चाहिए कि यह से ओवरराइड करता है C.fके बाद से Bइस विधि अधिभावी बंद करने का फैसला हो सकता है, और है कि नीचे की ओर अद्यतन में परिणाम नहीं चाहिए।

  • आपके पास यह A.fघोषित करने वाला पैटर्न नहीं है कि यह ओवरराइड करता है C.f, लेकिन B.fइसके ओवरराइड की घोषणा नहीं करता है।

  • आपके पास यह A.fघोषित करने वाला पैटर्न नहीं है कि यह ओवरराइड करता है C.f, लेकिन B.fयह घोषणा करता है कि यह कुछ से ओवरराइड करता है D.f

इसमें अमूर्त पद्धति को लागू करने के लिए अंकन और जाँच करने की विभिन्न विशेषताएं भी हैं।


0

जावा कक्षाओं के साथ जाइथॉन के तहत सुनवाई सबसे सरल और काम कर रही है:

class MyClass(SomeJavaClass):
     def __init__(self):
         setattr(self, "name_of_method_to_override", __method_override__)

     def __method_override__(self, some_args):
         some_thing_to_do()

0

न केवल डेकोरेटर मैंने चेक किया था यदि ओवरराइडिंग विशेषता का नाम किसी भी वर्ग का सुपरक्लास है, तो सुपरक्लास निर्दिष्ट किए बिना यह विशेषता है, यह डेकोरेटर यह सुनिश्चित करने के लिए भी जांच करता है कि ओवरराइडिंग विशेषता उसी तरह होनी चाहिए जो ओवरराइड की गई हो विशेषता। क्लास मेथड्स को मेथड्स की तरह ट्रीट किया जाता है और स्टैटिक मेथड्स को फंक्शन की तरह ट्रीट किया जाता है। यह डेकोरेटर कॉलिबल्स, क्लास मेथड्स, स्टैटिक मेथड्स और प्रॉपर्टीज के लिए काम करता है।

स्रोत कोड के लिए देखें: https://github.com/fireuser909/override

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


0

पायथन 2.6+ और पायथन 3.2+ में आप यह कर सकते हैं ( वास्तव में इसका अनुकरण करते हैं , पायथन फंक्शन ओवरलोडिंग का समर्थन नहीं करता है और चाइल्ड क्लास स्वचालित रूप से माता-पिता की विधि को ओवरराइड करता है)। हम इसके लिए डेकोरेटर्स का उपयोग कर सकते हैं। लेकिन पहले, ध्यान दें कि पायथन @decoratorsऔर जावा @Annotationsपूरी तरह से अलग चीजें हैं। पूर्ववर्ती ठोस कोड के साथ एक आवरण है, जबकि बाद में एक संकलक के लिए एक झंडा है।

इसके लिए पहले करें pip install multipledispatch

from multipledispatch import dispatch as Override
# using alias 'Override' just to give you some feel :)

class A:
    def foo(self):
        print('foo in A')

    # More methods here


class B(A):
    @Override()
    def foo(self):
        print('foo in B')
    
    @Override(int)
    def foo(self,a):
        print('foo in B; arg =',a)
        
    @Override(str,float)
    def foo(self,a,b):
        print('foo in B; arg =',(a,b))
        
a=A()
b=B()
a.foo()
b.foo()
b.foo(4)
b.foo('Wheee',3.14)

उत्पादन:

foo in A
foo in B
foo in B; arg = 4
foo in B; arg = ('Wheee', 3.14)

ध्यान दें कि आपको कोष्ठक के साथ यहां डेकोरेटर का उपयोग करना होगा

याद रखने वाली एक बात यह है कि चूंकि पायथन में सीधे तौर पर ओवरलोडिंग का कार्य नहीं होता है, इसलिए भले ही क्लास बी क्लास ए से विरासत में नहीं मिला हो, लेकिन उन सभी की जरूरत है जो fooआपको @ ऑवरराइड का उपयोग करने की आवश्यकता है (हालांकि उर्फ ​​'अधिभार' का उपयोग करने पर दिखेगा) उस मामले में बेहतर)

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