예를 들어 Java에서 @Override
주석은 재정의에 대한 컴파일 타임 확인 기능을 제공 할뿐만 아니라 자체 문서화 코드도 우수합니다.
나는 단지 문서를 찾고 있습니다 (pylint와 같은 체커에 대한 지표이지만 보너스입니다). 어딘가에 주석이나 docstring을 추가 할 수 있지만 파이썬에서 재정의를 나타내는 관용적 방법은 무엇입니까?
예를 들어 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!!!!!!!
overrides
재정의 메소드에 자체 메소드가 없으면 재정의 된 메소드의 docstring을 복사 할 수 있습니다.
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처럼 작성합니까?
override
데코레이터 에 실제 유효성 검사를 추가 할 수 있습니다 .
파이썬은 자바가 아니다. 물론 컴파일 타임 확인과 같은 것은 없습니다.
나는 docstring의 주석이 충분하다고 생각합니다. 이를 통해 메소드의 모든 사용자가 입력 help(obj.method)
하고 메소드가 대체임을 확인할 수 있습니다.
를 사용하여 인터페이스를 명시 적으로 확장 할 수도 있습니다. class Foo(Interface)
이렇게하면 사용자가 입력 help(Interface.method)
하여 메소드가 제공 할 기능에 대한 아이디어를 얻을 수 있습니다.
@Override
Java 의 실제 요점은 문서화하는 것이 아닙니다. 메소드를 재정의하려고 할 때 실수를 범하지만 새로운 메소드를 정의하게되었습니다 (예 : 철자가 틀 렸기 때문에; Java에서는 사용했기 때문에 발생할 수도 있습니다) 잘못된 서명이지만 파이썬에서는 문제가되지 않지만 철자가 잘못되었습니다.)
@Override
컴파일 시간 검사 외에 설명서입니다.
@ mkorpela 위대한 대답에 개선 , 여기에 버전이 있습니다
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
오류가 더 빨리 잡히는 것을 의미 합니다.
overrides.py
. 예외 유형을에서 (으) TypeError
로 변경하는 것 외에 다른 개선 할 점이 확실하지 않습니다 NotImplementedError
.
types.MethodType
. 그것은 당신의 대답에 좋은 생각이었습니다.
다른 사람들이 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
.
또한 추상 메소드 구현을 표시하고 확인하기위한 다양한 기능이 있습니다.
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()
오버라이드 속성의 이름이 슈퍼 클래스를 지정하지 않고 속성이 속해있는 클래스의 슈퍼 클래스인지 확인한 데코레이터뿐만 아니라,이 데코레이터는 오버라이드 속성과 오버라이드 속성이 같은 타입이어야합니다. 속성. 클래스 메소드는 메소드처럼 처리되고 정적 메소드는 함수처럼 처리됩니다. 이 데코레이터는 콜 러블, 클래스 메서드, 정적 메서드 및 속성에 작동합니다.
소스 코드는 https://github.com/fireuser909/override를 참조하십시오.
이 데코레이터는 override의 인스턴스 인 클래스에만 작동합니다. 테스트하려면 override .__ init__ 모듈을 실행하십시오.
파이썬 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'라는 별칭이 사용됩니다. 이 경우에 더 좋습니다)