'NotImplementedError' का उपयोग कब करें?


108

क्या कक्षा को सही ढंग से लागू करने के लिए खुद को और अपनी टीम को याद दिलाना है? मुझे इस तरह एक सार वर्ग का उपयोग पूरी तरह से नहीं मिलता है:

class RectangularRoom(object):
    def __init__(self, width, height):
        raise NotImplementedError

    def cleanTileAtPosition(self, pos):
        raise NotImplementedError

    def isTileCleaned(self, m, n):
        raise NotImplementedError

5
क्रॉस-साइट
डुपी

2
मैं कहूंगा: जब यह "कम से कम विस्मय का सिद्धांत" को संतुष्ट करता है ।
MSeifert

3
यह एक उपयोगी प्रश्न है, जैसा कि डॉक्स के अनुसार उरीएल द्वारा स्पष्ट उत्तर से, और जेरेम द्वारा अमूर्त आधार वर्गों के बारे में मूल्य-वर्धित उत्तर।
दावोस

यह इंगित करने के लिए भी इस्तेमाल किया जा सकता है कि एक व्युत्पन्न वर्ग जानबूझकर बेस क्लास सार पद्धति को लागू नहीं करता है, जो दो-तरफ़ा सुरक्षा प्रदान कर सकता है। नीचे देखें
pfabri

जवाबों:


80

जैसा कि दस्तावेज में कहा गया है [डॉक्स] ,

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

ध्यान दें कि हालांकि मुख्य उपयोग का मामला यह त्रुटि सार विधियों का संकेत है जो विरासत में मिली कक्षाओं पर लागू किया जाना चाहिए, आप इसे किसी भी तरह से उपयोग कर सकते हैं, जैसे कि एक TODOमार्कर के संकेत के लिए ।


6
अमूर्त तरीकों के लिए, मैं उपयोग करना पसंद करता हूं abc( मेरा उत्तर देखें )।
Jérôme

@ Jérôme दोनों का उपयोग क्यों नहीं करते? से सजाएं abstractmethodऔर ऊपर उठाएं NotImplementedError। यह व्युत्पन्न वर्गों के super().method()कार्यान्वयन में निषिद्ध है method
टाइमजैब

@timgeg मुझे सुपर () विधि () को प्रतिबंधित करने की आवश्यकता नहीं है।
Jérôme

51

जैसा कि उरीएल कहते हैं , यह एक अमूर्त वर्ग में एक विधि के लिए है जिसे बाल वर्ग में लागू किया जाना चाहिए, लेकिन इसका उपयोग TODO को इंगित करने के लिए भी किया जा सकता है।

पहले उपयोग के मामले के लिए एक विकल्प है: सार बेस कक्षाएं । वे अमूर्त कक्षाएं बनाने में मदद करते हैं।

यहाँ एक पायथन 3 उदाहरण है:

class C(abc.ABC):
    @abc.abstractmethod
    def my_abstract_method(self, ...):
        ...

तत्काल करते समय C, आपको एक त्रुटि मिलेगी क्योंकि my_abstract_methodसार है। आपको इसे बाल वर्ग में लागू करने की आवश्यकता है।

TypeError: Can't instantiate abstract class C with abstract methods my_abstract_method

उपवर्ग Cऔर कार्यान्वयन my_abstract_method

class D(C):
    def my_abstract_method(self, ...):
        ...

अब तुम झटपट कर सकते हो D

C.my_abstract_methodखाली होना नहीं है। इसे Dइस्तेमाल करने से बुलाया जा सकता है super()

इस ओवर NotImplementedErrorका एक फायदा यह है कि आपको Exceptionतात्कालिकता समय पर मिलती है , न कि विधि कॉल समय पर।


2
पाइथन 2.6+ में भी उपलब्ध है। बस from abc import ABCMeta, abstractmethodऔर अपने एबीसी को परिभाषित करें __metaclass__ = ABCMeta। डॉक्स: docs.python.org/2/library/abc.html
BoltzmannBrain

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

27

इसके बजाय विचार करें कि क्या यह था:

class RectangularRoom(object):
    def __init__(self, width, height):
        pass

    def cleanTileAtPosition(self, pos):
        pass

    def isTileCleaned(self, m, n):
        pass

और आप उपवर्ग और यह बताना भूल जाते हैं कि कैसे isTileCleaned(), शायद अधिक संभावना है, इसे टाइप करें isTileCLeaned()। फिर आपके कोड में, Noneजब आप इसे कॉल करेंगे तो आपको मिलेगा ।

  • क्या आप चाहते थे कि आप को ओवरराइड फंक्शन मिलेगा? निश्चित रूप से नहीं।
  • है Noneवैध उत्पादन? कौन जाने।
  • क्या वह इच्छित व्यवहार है? लगभग निश्चित रूप से नहीं।
  • क्या आपको कोई त्रुटि मिलेगी? निर्भर करता है।

raise NotImplmentedError आपको इसे लागू करने के लिए मजबूर करता है, क्योंकि जब तक आप ऐसा करने की कोशिश नहीं करेंगे तब तक यह एक अपवाद को फेंक देगा । यह बहुत सारी मौन त्रुटियों को दूर करता है। यह एक नंगे को छोड़कर क्यों समान है लगभग एक अच्छा विचार नहीं है : क्योंकि लोग गलती करते हैं और यह सुनिश्चित करता है कि वे गलीचा के नीचे बह नहीं रहे हैं।

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


11

एक- आधारित बेस क्लास विधि की बाल पद्धति के raise NotImplementedError() अंदर भी कर सकता है @abstractmethod


माप मॉड्यूल (भौतिक उपकरणों) के एक परिवार के लिए एक नियंत्रण स्क्रिप्ट लिखने की कल्पना करें। प्रत्येक मॉड्यूल की कार्यक्षमता को संकीर्ण रूप से परिभाषित किया गया है, जो केवल एक समर्पित फ़ंक्शन को कार्यान्वित कर रहा है: एक रिले का एक सरणी हो सकता है, दूसरा मल्टी-चैनल डीएसी या एडीसी, दूसरा एक एमीटर।

उपयोग में आने वाले निम्न-स्तरीय कमांडों में से अधिकांश को उनके आईडी नंबर पढ़ने या उन्हें कमांड भेजने के लिए उदाहरण के लिए मॉड्यूल के बीच साझा किया जाएगा। आइए देखें कि हमारे पास इस बिंदु पर क्या है:

बेस क्लास

from abc import ABC, abstractmethod  #< we'll make use of these later

class Generic(ABC):
    ''' Base class for all measurement modules. '''

    # Shared functions
    def __init__(self):
        # do what you must...

    def _read_ID(self):
        # same for all the modules

    def _send_command(self, value):
        # same for all the modules

क्रियाओं को साझा किया

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

  • get(channel)

  • रिले: रिले की ऑन / ऑफ स्थिति प्राप्त करेंchannel

  • डीएसी: प्राप्त उत्पादन पर वोल्टेजchannel

  • एडीसी: इनपुट वोल्टेज प्राप्त करेंchannel

  • enable(channel)

  • रिले: रिले के उपयोग को सक्षम करेंchannel

  • DAC: आउटपुट चैनल के उपयोग को सक्षम करेंchannel

  • एडीसी: इनपुट चैनल के उपयोग को सक्षम करेंchannel

  • set(channel)

  • रिले: रिले को channelचालू / बंद सेट करें

  • DAC: आउटपुट वोल्टेज को ऑन पर सेट करेंchannel

  • एडीसी: हम्म ... कुछ भी तार्किक दिमाग में नहीं आता है।


साझा क्रिया-कलापों को लागू किया गया

मेरा तर्क है कि उपरोक्त क्रियाओं को मॉड्यूल में साझा करने के लिए एक मजबूत मामला है क्योंकि हमने देखा कि उनका अर्थ उनमें से प्रत्येक के लिए स्पष्ट है। मैं अपना बेस क्लास लिखना जारी रखूँगा Generic:

class Generic(ABC):  # ...continued
    
    @abstractmethod
    def get(self, channel):
        pass

    @abstractmethod
    def enable(self, channel):
        pass

    @abstractmethod
    def set(self, channel):
        pass

उपवर्गों

अब हम जानते हैं कि हमारे उपवर्गों को इन विधियों को परिभाषित करना होगा। आइए देखें कि यह एडीसी मॉड्यूल के लिए कैसा दिख सकता है:

class ADC(Generic):

    def __init__(self):
        super().__init__()  #< applies to all modules
        # more init code specific to the ADC module
    
    def get(self, channel):
        # returns the input voltage measured on the given 'channel'

    def enable(self, channel):
        # enables accessing the given 'channel'

अब आप सोच रहे होंगे:

लेकिन यह एडीसी मॉड्यूल के लिए काम नहीं करेगा क्योंकि इसका setकोई मतलब नहीं है क्योंकि हमने इसे ऊपर देखा है!

आप सही हैं: लागू setनहीं करना एक विकल्प नहीं है क्योंकि जब आप अपने एडीसी ऑब्जेक्ट को तुरंत हटाने की कोशिश करते हैं तो पायथन नीचे त्रुटि करेगा।

TypeError: Can't instantiate abstract class 'ADC' with abstract methods 'set'

तो आपको कुछ लागू करना होगा, क्योंकि हमने setएक लागू क्रिया (उर्फ '@abbridmethod') की है, जिसे दो अन्य मॉड्यूल द्वारा साझा किया गया है, लेकिन साथ ही, आपको कुछ भी लागू नहीं करना चाहिए setक्योंकि इस विशेष मॉड्यूल के लिए कोई मतलब नहीं है।

बचाव के लिए NotImplementedError

एडीसी वर्ग को इस तरह पूरा करके:

class ADC(Generic): # ...continued

    def set(self, channel):
        raise NotImplementedError("Can't use 'set' on an ADC!")

आप एक साथ तीन बहुत अच्छी चीजें कर रहे हैं:

  1. आप किसी उपयोगकर्ता को गलत तरीके से एक कमांड ('सेट') जारी करने से बचा रहे हैं जो इस मॉड्यूल के लिए लागू नहीं है (और नहीं!)।
  2. आप उन्हें स्पष्ट रूप से बता रहे हैं कि समस्या क्या है (यह क्यों महत्वपूर्ण है इसके लिए 'बेयर अपवाद' के बारे में टेम्पोरलवुल्फ़ लिंक देखें)
  3. आप उन सभी अन्य मॉड्यूल के कार्यान्वयन की रक्षा कर रहे हैं जिनके लिए लागू किए गए क्रियाओं का अर्थ है। यानी आप यह सुनिश्चित करें कि उन मॉड्यूल जिसके लिए इन क्रियाओं करना मेकअप भावना इन तरीकों को लागू करेगा और उस वे बिल्कुल इन क्रियाओं का उपयोग करके ऐसा करेंगे और न किसी और तदर्थ नाम।

-1

आप @propertyडेकोरेटर का उपयोग कर सकते हैं ,

>>> class Foo():
...     @property
...     def todo(self):
...             raise NotImplementedError("To be implemented")
... 
>>> f = Foo()
>>> f.todo
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 4, in todo
NotImplementedError: To be implemented

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