예를 들어 Java에서 @Override
주석은 재정의에 대한 컴파일 타임 확인 기능을 제공 할뿐만 아니라 자체 문서화 코드도 우수합니다.
나는 단지 문서를 찾고 있습니다 (pylint와 같은 체커에 대한 지표이지만 보너스입니다). 어딘가에 주석이나 docstring을 추가 할 수 있지만 파이썬에서 재정의를 나타내는 관용적 방법은 무엇입니까?
답변
이것과 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!!!!!!!
답변
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
답변
이를 문서화 목적으로 만 사용하려는 경우 자체 재정의 데코레이터를 정의 할 수 있습니다.
def override(f):
return f
class MyClass (BaseClass):
@override
def method(self):
pass
실제로 재정의를 확인하는 방식으로 override (f)를 만들지 않는 한 실제로 눈에 띄는 것은 아닙니다.
그러나 이것이 파이썬입니다. 왜 Java처럼 작성합니까?
답변
파이썬은 자바가 아니다. 물론 컴파일 타임 확인과 같은 것은 없습니다.
나는 docstring의 주석이 충분하다고 생각합니다. 이를 통해 메소드의 모든 사용자가 입력 help(obj.method)
하고 메소드가 대체임을 확인할 수 있습니다.
를 사용하여 인터페이스를 명시 적으로 확장 할 수도 있습니다. class Foo(Interface)
이렇게하면 사용자가 입력 help(Interface.method)
하여 메소드가 제공 할 기능에 대한 아이디어를 얻을 수 있습니다.
답변
@ 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
오류가 더 빨리 잡히는 것을 의미 합니다.
답변
다른 사람들이 Java와 달리 @Overide 태그는 없지만 위의 데코레이터를 사용하여 직접 만들 수는 있지만 내부 dict 대신 getattrib () 전역 메소드를 사용하여 다음과 같은 것을 얻을 것을 제안합니다.
def Override(superClass):
def method(func)
getattr(superClass,method.__name__)
return method
원하는 경우 getattr ()을 catch하여 자신의 오류를 잡아보십시오. 그러나이 경우 getattr 메소드가 더 좋습니다.
또한 클래스 메소드 및 Vairables를 포함하여 클래스에 바인딩 된 모든 항목을 잡습니다.
답변
@ 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
.
또한 추상 메소드 구현을 표시하고 확인하기위한 다양한 기능이 있습니다.
