Communities

Writing
Writing
Codidact Meta
Codidact Meta
The Great Outdoors
The Great Outdoors
Photography & Video
Photography & Video
Scientific Speculation
Scientific Speculation
Cooking
Cooking
Electrical Engineering
Electrical Engineering
Judaism
Judaism
Languages & Linguistics
Languages & Linguistics
Software Development
Software Development
Mathematics
Mathematics
Christianity
Christianity
Code Golf
Code Golf
Music
Music
Physics
Physics
Linux Systems
Linux Systems
Power Users
Power Users
Tabletop RPGs
Tabletop RPGs
Community Proposals
Community Proposals
tag:snake search within a tag
answers:0 unanswered questions
user:xxxx search by author id
score:0.5 posts with 0.5+ score
"snake oil" exact phrase
votes:4 posts with 4+ votes
created:<1w created < 1 week ago
post_type:xxxx type of post
Search help
Notifications
Mark all as read See all your notifications »
Q&A

Welcome to Software Development on Codidact!

Will you help us build our independent community of developers helping developers? We're small and trying to grow. We welcome questions about all aspects of software development, from design to code to QA and more. Got questions? Got answers? Got code you'd like someone to review? Please join us.

Use cases for raising a 'NotImplementedError' in Python

+6
−0

What are some legitimate use cases for raising a NotImplementedError exception in Python? How can it help express a code author's intentions to aid code maintenance and/or further development of code?

History
Why does this post require attention from curators or moderators?
You might want to add some details to your flag.
Why should this post be closed?

0 comment threads

3 answers

You are accessing this answer with a direct link, so it's being shown above all other answers regardless of its score. You can return to the normal view.

+5
−1

This is in the docs. To paraphrase:

  • Used for abstract methods that must be overridden in subclasses
  • When the implementation is still WIP, but you want to leave a placeholder for the method name and signature

It's a way to to "reserve namespace", but with the actual method body "coming soon" (from you or someone else).

History
Why does this post require attention from curators or moderators?
You might want to add some details to your flag.

0 comment threads

+5
−0

NotImplementedError should generally be viewed as indicating some design problem. You should not be reaching for it as a matter of course.

Here are some potential times you might feel a desire to reach for NotImplementedError.

  1. During development, you want to test some code that is partially implemented. Using NotImplementedError for the parts not yet implemented would be fine. Of course, these uses of NotImplementedError should be removed before you "release" the project.

  2. A third-party library requires you to implement all methods of a class to use its functionality, but some methods don't make sense for your use-case. If the documentation states that some methods don't need to be implemented, then you'd be fine (but they'd probably already have default implementations that raised NotImplementedError). If not, then you don't know if it is safe to not implement one of the methods. Using NotImplementedError in this case should be viewed as a hack that may well cause unknown issues now and/or in the future. Arguably, being in this situation either way indicates poor design in the third party library.

  3. You have a plugin system and a plugin may optionally provide certain functionality. NotImplementedError may seem like a good way to handle optional functionality that's not provided, but a better way is to query the plugin for different facets (or bundles) of coherent functionality. For example, if you had an IDE plugin that could support syntax highlighting or jump to definition among other things, you could have getSyntaxHighlighterFacet() and getJumpToDefinitionFacet() methods that the plugin must implement. These methods would return a coherent object if it supports that functionality, otherwise it would return None. This would allow the plugin loader to query what functionality is available and substitute a fallback for functionality that's not available. It would also avoid finding out some functionality is unavailable at the last possible moment where it might lead to unexpected behavior or require fallback logic to be written everywhere.

History
Why does this post require attention from curators or moderators?
You might want to add some details to your flag.

1 comment thread

what about abstract methods? (2 comments)
+0
−1

One of the usecases I have found very useful is to do a raise NotImplementedError() inside the child method of an @abstractmethod-decorated base class method. Yes, it's a mouthful but what it really means is graceful simplicity.


Example

Imagine writing a control script for a family of electronic measurement modules (i.e. physical devices). The functionality of each module is narrowly-defined, implementing just one dedicated function: one could be an array of relays, another a multi-channel DAC or ADC, another an ammeter etc.

Many of the low-level commands would be shared between the modules for example to read their ID numbers or to send a command to them. Let's see what we have at this point:

Base Class

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

Shared Verbs

We then realise that much of the module-specific command verbs and, therefore, the logic of their interfaces is also shared. Here are 3 different verbs whose meaning would be self-explanatory considering a number of target modules.

get(channel):

  • relay: get the on/off status of the relay on channel
  • DAC: get the output voltage on channel
  • ADC: get the input voltage on channel

enable(channel):

  • relay: enable the use of the relay on channel
  • DAC: enable the use of the output channel on channel
  • ADC: enable the use of the input channel on channel

set(channel):

  • relay: set the relay on channel on/off
  • DAC: set the output voltage on channel
  • ADC: hmm... nothing logical comes to mind.

Shared Verbs Become Enforced Verbs

I'd argue that there is a strong case for the above verbs to be shared across the modules as we saw that their meaning is evident for each one of them. I'd continue writing my base class Generic like so:

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

    @abstractmethod
    def enable(self, channel):
        pass

    @abstractmethod
    def set(self, channel):
        pass

Subclasses

We now know that our subclasses will all have to define these methods. Let's see what it could look like for the ADC module:

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 value measured on the given 'channel'

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

You may now be wondering:

But this won't work for the ADC module as set makes no sense there!

You're right: NOT implementing set is not an option as Python would then fire the error below at you as soon as your as you tried instantiating your ADC object.

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

So you must implement set, because we made it an enforced verb (aka @abstractmethod), which is shared by two other modules. But, what should you implement if set does not make sense for this particular module? What makes the interface (for users) and the code-base (for future maintenance) as clean as possible?

NotImplementedError to the Rescue

By completing the ADC class like this:

class ADC(Generic): # ...continued

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

You are doing three very good things at once:

  1. You are protecting a user from erroneously issuing a command ('set') that is not (and shouldn't!) be implemented for this module.
  2. You are telling them explicitly what the problem is (see this link about Bare exceptions for why this is important) (originally linked by TemporalWolf)
  3. You are protecting the implementation of all the other modules for which the enforced verbs do make sense. I.e. you ensure that those modules for which these verbs do make sense will implement these methods and that they will do so using exactly these verbs and not some other ad-hoc names.
History
Why does this post require attention from curators or moderators?
You might want to add some details to your flag.

3 comment threads

If you want to allow not implementing set(self, channel), why would you declare it as required to imp... (1 comment)
Band-aid on bad design (1 comment)
TemporalWolf's missing link? (1 comment)

Sign up to answer this question »