'NotImplementedError'를 언제 사용합니까?


109

자신과 팀이 수업을 올바르게 수행하도록 상기시키는 것입니까? 다음과 같은 추상 클래스를 완전히 사용하지 못했습니다.

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
이것은 문서에 따라 Uriel의 명확한 답변과 Jérôme의 추상 기본 클래스에 대한 가치 부가 답변으로 입증되는 유용한 질문입니다.
Davos

파생 클래스가 의도적으로 양방향 보호를 제공 할 수있는 기본 클래스 추상 메서드를 구현하지 않음을 나타내는 데 사용할 수도 있습니다. 아래를 참조하십시오 .
pfabri

답변:


81

문서에서 [docs] ,

사용자 정의 기본 클래스에서 추상 메서드는 메서드를 재정의하기 위해 파생 클래스가 필요하거나 클래스가 개발되는 동안 실제 구현을 추가해야 함을 나타 내기 위해이 예외를 발생시켜야합니다.

이 오류는 상속 된 클래스에서 구현되어야하는 추상 메서드를 나타내는 주요 사용 사례이지만 TODO마커 표시를 위해 원하는대로 사용할 수 있습니다 .


6
추상 방법의 경우 사용하는 것을 선호합니다 abc( 내 답변 참조 ).
Jérôme

@ Jérôme 왜 둘 다 사용하지 않습니까? 로 꾸미고 abstractmethod올리십시오 NotImplementedError. 이것은 파생 클래스의 super().method()구현을 금지 method합니다.
timgeb

@timgeg 나는 super (). method ()를 금지 할 필요성을 느끼지 않습니다.
Jérôme

52

으로 우리엘은 말한다 , 그것은 자식 클래스에서 구현해야하는 추상 클래스의 메소드에 대한 의미하지만,뿐만 아니라 TODO를 표시하는 데 사용할 수 있습니다.

첫 번째 사용 사례에 대한 대안이 있습니다 : Abstract Base Classes . 그것들은 추상 클래스를 만드는 데 도움이됩니다.

다음은 Python 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
Python 2.6 이상에서도 사용할 수 있습니다. 그냥 from abc import ABCMeta, abstractmethod하고와 ABC를 정의합니다 __metaclass__ = ABCMeta. 문서 : docs.python.org/2/library/abc.html
BoltzmannBrain

이를 메타 클래스를 정의하는 클래스와 함께 사용하려면 클래스의 원래 메타 클래스와 ABCMeta를 모두 상속하는 새 메타 클래스를 만들어야합니다.
rabbit.aaron

27

대신 다음과 같은 경우 고려하십시오.

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

    def cleanTileAtPosition(self, pos):
        pass

    def isTileCleaned(self, m, n):
        pass

그리고 당신은 하위 클래스를 만들고 isTileCleaned()그것을 isTileCLeaned(). 그런 다음 코드에서 None호출 하면 a가 표시 됩니다.

  • 원하는 재정의 된 기능을 얻을 수 있습니까? 절대 아니다.
  • None유효 출력은? 누가 알아.
  • 의도 된 행동입니까? 거의 확실하지 않습니다.
  • 오류가 발생합니까? 때에 따라 다르지.

raise NotImplmentedError 은 당신이 그것을 구현하는 것입니다 당신이 이렇게까지 그것을 실행하려고하면 예외를 throw합니다. 이것은 많은 침묵 오류를 제거합니다. 맨손을 제외하고는 거의 좋은 생각이 아닌 이유와 비슷합니다 . 사람들은 실수를해서 깔개 아래로 휩쓸 리지 않도록하기 때문입니다.

참고 : 다른 답변에서 언급했듯이 추상 기본 클래스를 사용하는 것이 더 낫습니다. 그러면 오류가 프런트로드되고 프로그램이 구현 될 때까지 프로그램이 실행되지 않기 때문입니다 (NotImplementedError를 사용하면 실제로 호출되는 경우에만 예외가 발생합니다).


11

-decorated 기본 클래스 메서드 의 자식 메서드 raise NotImplementedError() 내부 에서 수행 할 수도 있습니다 @abstractmethod.


측정 모듈 (물리적 장치) 제품군에 대한 제어 스크립트를 작성한다고 상상해보십시오. 각 모듈의 기능은 좁게 정의되어 하나의 전용 기능 만 구현합니다. 하나는 릴레이 어레이, 다른 하나는 다중 채널 DAC 또는 ADC, 다른 하나는 전류계 등일 수 있습니다.

사용중인 대부분의 저수준 명령은 모듈간에 공유되어 ID 번호를 읽거나 명령을 보냅니다. 이 시점에서 우리가 가진 것을 보자 :

기본 클래스

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)

  • 릴레이 : 릴레이 의 on / off 상태를 가져옵니다.channel

  • DAC : 얻가 출력 전압에를channel

  • ADC : 얻가 입력 전압에를channel

  • enable(channel)

  • 릴레이 : 릴레이 사용 활성화channel

  • DAC : 출력 채널 사용 활성화channel

  • ADC : 입력 채널 사용 활성화channel

  • set(channel)

  • 릴레이 : 릴레이 channel켜기 / 끄기 설정

  • DAC : 설정된 출력 전압 온channel

  • ADC : 음 ... 논리적으로 떠오르는 것은 없습니다 .


공유 동사가 강제 동사가 됨

위의 동사가 각 모듈에 대해 그 의미가 분명하다는 것을 알기 때문에 모듈 전체에서 공유되는 강력한 사례가 있다고 주장합니다. 다음 Generic과 같이 기본 클래스를 계속 작성합니다 .

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

    @abstractmethod
    def enable(self, channel):
        pass

    @abstractmethod
    def set(self, channel):
        pass

하위 클래스

이제 우리는 서브 클래스가 모두 이러한 메서드를 정의해야한다는 것을 알고 있습니다. ADC 모듈의 모습을 살펴 보겠습니다.

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'

이제 궁금 할 것입니다.

그러나 이것은 위에서 본 것처럼 ADC 모듈 에서는 작동하지 않습니다 set.

맞습니다 : 구현 set하지 않는 것은 Python이 ADC 개체를 인스턴스화하려고 할 때 아래 오류를 발생시키기 때문에 옵션이 아닙니다.

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

따라서 다른 두 모듈에서 공유 set하는 강제 동사 ( '@abstractmethod'라고도 함)를 만들었 기 때문에 무언가를 구현해야 하지만 동시에이 set특정 모듈에 대해 의미가없는 것은 구현 하지 않아야합니다.

구조에 NotImplementedError

다음과 같이 ADC 클래스를 완료하면 :

class ADC(Generic): # ...continued

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

한 번에 세 가지 좋은 일을하고 있습니다.

  1. 이 모듈에 대해 구현되어서는 안되는 명령 ( 'set')을 잘못 실행하지 못하도록 사용자를 보호하고 있습니다.
  2. 문제가 무엇인지 명시 적으로 말하고 있습니다 (이것이 중요한 이유는 '베어 예외'에 대한 TemporalWolf의 링크 참조).
  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
나는 이것이 언제 그것을 사용 해야하는지에 대한 질문을 어떻게 해결하는지 알지 못합니다 .
TemporalWolf
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.