파이썬에서 메소드를 재정의하고 있음을 어떻게 표시합니까?


173

예를 들어 Java에서 @Override주석은 재정의에 대한 컴파일 타임 확인 기능을 제공 할뿐만 아니라 자체 문서화 코드도 우수합니다.

나는 단지 문서를 찾고 있습니다 (pylint와 같은 체커에 대한 지표이지만 보너스입니다). 어딘가에 주석이나 docstring을 추가 할 수 있지만 파이썬에서 재정의를 나타내는 관용적 방법은 무엇입니까?


13
다시 말해서, 당신은 당신이 메소드를 오버라이드하고 있다는 것을 나타내지 않는가? 그것을 스스로 알아 내기 위해 독자에게 맡기십시오.
Bluu

2
예, 컴파일 된 언어에서 오류가 발생하기 쉬운 상황처럼 보이지만 받아 들여야합니다. 실제로 나는 그것이 큰 문제가 아니라는 것을 알지 못했다 (필자의 경우 루비가 아니라 같은 생각이다)
Ed S.

물론입니다. Triptych의 대답과 mkorpela의 대답은 간단하지만, 나는 그것을 좋아하지만 후자의 명시 적-암묵적 정신과 실수를 지능적으로 방지하는 것이 승리합니다.
Bluu

1
직접적으로 동일하지는 않지만 추상 기본 클래스는 모든 추상 메서드가 하위 클래스로 재정의되었는지 확인합니다. 물론 구체적인 방법을 재정의하는 경우 도움이되지 않습니다.
letmaik

답변:


206

이것과 fwc : s 답변에 따라 pip 설치 가능 패키지를 만들었습니다 https://github.com/mkorpela/overrides

때때로 나는이 질문을보고 여기에 끝납니다. 주로 이것은 코드베이스에서 동일한 버그를 본 후에 다시 발생합니다. 누군가 "인터페이스"에서 메소드의 이름을 바꾸는 동안 클래스를 구현하는 "인터페이스"를 잊었습니다.

파이썬은 Java는 아니지만 파이썬은 힘을 가지고 있으며 명시 적은 암시 적보다 낫습니다. 실제 세계 에서이 일이 도움이되었을만한 구체적인 사례가 있습니다.

오버라이드 데코레이터의 스케치입니다. 이것은 매개 변수로 주어진 클래스가 장식 될 메소드와 같은 메소드 (또는 무언가) 이름을 가지고 있는지 확인합니다.

더 나은 솔루션을 생각할 수 있다면 여기에 게시하십시오!

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!!!!!!!

17
대박. 처음 시도했을 때 맞춤법 오류가 발생했습니다. 명성.
Christopher Bruns

7
mfbutner : 메소드가 실행될 때마다 호출되지 않으며 메소드가 작성 될 때만 호출됩니다.
mkorpela

3
이것은 doc 문자열에도 좋습니다! overrides재정의 메소드에 자체 메소드가 없으면 재정의 된 메소드의 docstring을 복사 할 수 있습니다.
letmaik

5
@ mkorpela, Heh이 코드는 파이썬 기본 lib 시스템에 있어야합니다. 왜 이것을 pip 시스템에 넣지 않습니까? : P

5
@ mkorpela : 아, 나는 파이썬 핵심 개발자 에게이 패키지를 알려주지 말라고 제안한다. 핵심 파이썬 시스템에 오버라이드 데코레이터를 추가하는 것을 고려하지 않을 수도있다. :)

30

interface_class 이름을 지정하지 않아도되는 구현은 다음과 같습니다.

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

@decormethod 또는 @property와 같은 내장 데코레이터와 비교하여이 데코레이터를 사용하는 데 소요되는 평균 및 최악의 비용은 얼마입니까?
larham1

4
@ larham1이 데코레이터는 각 호출이 아닌 클래스 정의가 분석 될 때 한 번 실행됩니다. 따라서 프로그램 런타임과 비교할 때 실행 비용은 관련이 없습니다.
Abgan

이것은 PEP 487 덕분에 Python 3.6에서 훨씬 좋을 것입니다 .
Neil G

더 나은 오류 메시지를 얻으려면 any (assertr (cls, method .__ name__) for cls for cls for bases), 'Overriden method "{}"을 (를) 기본 클래스에서 찾을 수 없습니다.'. format (method .__ name__)
Ivan Kovtun

14

이를 문서화 목적으로 만 사용하려는 경우 자체 재정의 데코레이터를 정의 할 수 있습니다.

def override(f):
    return f


class MyClass (BaseClass):

    @override
    def method(self):
        pass

실제로 재정의를 확인하는 방식으로 override (f)를 만들지 않는 한 실제로 눈에 띄는 것은 아닙니다.

그러나 이것이 파이썬입니다. 왜 Java처럼 작성합니까?


2
검사를 통해 override데코레이터 에 실제 유효성 검사를 추가 할 수 있습니다 .
Erik Kaplun

70
그러나 이것이 파이썬입니다. 왜 Java처럼 작성합니까? Java의 일부 아이디어가 좋고 다른 언어로 확장 할 가치가 있기 때문에?
Piotr Dobrogost

9
수퍼 클래스에서 메소드의 이름을 바꿀 때 일부 서브 클래스 2 레벨이 메소드를 대체했음을 알면 좋을 것입니다. 물론 확인하기는 쉽지만 언어 파서의 도움이 아프지 않을 것입니다.
Abgan

4
좋은 생각 이니까 다양한 다른 언어가 기능을 가지고 있다는 사실은 논쟁의 여지가 없습니다.
sfkleach

6

파이썬은 자바가 아니다. 물론 컴파일 타임 확인과 같은 것은 없습니다.

나는 docstring의 주석이 충분하다고 생각합니다. 이를 통해 메소드의 모든 사용자가 입력 help(obj.method)하고 메소드가 대체임을 확인할 수 있습니다.

를 사용하여 인터페이스를 명시 적으로 확장 할 수도 있습니다. class Foo(Interface)이렇게하면 사용자가 입력 help(Interface.method)하여 메소드가 제공 할 기능에 대한 아이디어를 얻을 수 있습니다.


57
@OverrideJava 의 실제 요점은 문서화하는 것이 아닙니다. 메소드를 재정의하려고 할 때 실수를 범하지만 새로운 메소드를 정의하게되었습니다 (예 : 철자가 틀 렸기 때문에; Java에서는 사용했기 때문에 발생할 수도 있습니다) 잘못된 서명이지만 파이썬에서는 문제가되지 않지만 철자가 잘못되었습니다.)
Pavel Minaev

2
@ Pavel Minaev : 사실이지만, 특히 재정의에 대한 자동 표시기가없는 IDE / 텍스트 편집기를 사용하는 경우 문서를 작성하는 것이 여전히 편리합니다 (예 : Eclipse의 JDT는 행 번호와 함께 깔끔하게 표시).
Tuukka Mustonen

2
@PavelMinaev 잘못되었습니다. 주요 시점 중 하나는 @Override컴파일 시간 검사 외에 설명서입니다.
siamii

6
@siamii 나는 문서에 대한 도움이 훌륭하다고 생각하지만, 내가 본 모든 공식 Java 문서에서 컴파일 시간 검사의 중요성만을 나타냅니다. Pavel이 "틀렸다"는 주장을 입증하십시오.
Andrew Mellinger 2018 년

5

@ mkorpela 위대한 대답에 개선 , 여기에 버전이 있습니다

보다 정확한 점검, 이름 지정 및 발생한 Error 객체

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 프로젝트에서 풀 요청을 고려할 수 있습니까? 답변을 추가했습니다.
Neil G

@ NeilG 나는 ipromise 프로젝트를 포크하고 약간 코딩했다. 본질적으로 이것을 내에서 구현 한 것 같습니다 overrides.py. 예외 유형을에서 (으) TypeError로 변경하는 것 외에 다른 개선 할 점이 확실하지 않습니다 NotImplementedError.
JamesThomasMoon1979

야! 감사합니다. 재정의 된 객체에 실제로 유형이 있는지 확인하지 못했습니다 types.MethodType. 그것은 당신의 대답에 좋은 생각이었습니다.
Neil G

2

다른 사람들이 Java와 달리 @Overide 태그는 없지만 위의 데코레이터를 사용하여 직접 만들 수는 있지만 내부 dict 대신 getattrib () 전역 메소드를 사용하여 다음과 같은 것을 얻을 것을 제안합니다.

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

원하는 경우 getattr ()을 catch하여 자신의 오류를 잡아보십시오. 그러나이 경우 getattr 메소드가 더 좋습니다.

또한 클래스 메소드 및 Vairables를 포함하여 클래스에 바인딩 된 모든 항목을 잡습니다.


2

@ mkorpela의 훌륭한 대답을 바탕으로 더 많은 검사를 수행 하는 비슷한 패키지 ( ipromise pypi github )를 작성했습니다 .

A에서 상속 B하고 C에서 B상속 한다고 가정합니다 C.

모듈 ipromise 는 다음을 확인합니다.

  • 경우 A.f재정의 B.f, B.f존재해야하며, A상속합니다 B. (이것은 재정의 패키지에서 확인한 것입니다).

  • 패턴 A.f을 재정의 B.f한다고 선언 한 다음 이 패턴 을 재정의한다고 선언 하지 않습니다 C.f. A그것에서보다 우선 말을해야 C.f하기 때문에 B이 방법을 무시 중지하기로 결정 수 있으며 그 하류 업데이트 초래해서는 안된다.

  • 당신은 패턴이없는 A.f이 우선 함을 선언 C.f하지만, B.f그 재정의를 선언하지 않습니다.

  • 패턴 A.f을 재정의 한다는 선언은 C.f없지만 B.f일부 패턴 을 재정의 한다고 선언합니다 D.f.

또한 추상 메소드 구현을 표시하고 확인하기위한 다양한 기능이 있습니다.


0

Java 클래스를 사용하여 Jython에서 가장 간단하고 작동합니다.

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를 참조하십시오.

이 데코레이터는 override의 인스턴스 인 클래스에만 작동합니다. 테스트하려면 override .__ 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)

여기에 괄호와 함께 데코레이터를 사용해야합니다

한 가지 기억해야 할 것은 파이썬에는 함수 오버로딩이 없기 때문에 클래스 B가 클래스 A에서 상속하지 않지만 foo@Override를 사용해야하는 것보다 모든 것을 필요로하는 경우에도 별칭을 사용하면 'Overload'라는 별칭이 사용됩니다. 이 경우에 더 좋습니다)

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.