Em Python, como faço para indicar que eu estou substituindo um método?
-
19-09-2019 - |
Pergunta
Em Java, por exemplo, a anotação @Override
não só fornece a verificação em tempo de compilação de uma substituição, mas faz para excelente código de auto-documentar.
Estou apenas à procura de documentação (embora se é um indicador para alguns verificador como pylint, isso é um bônus). I pode adicionar um comentário ou DocString em algum lugar, mas o que é a maneira idiomática para indicar uma substituição em Python?
Solução
UPDATE (2015/05/23): Com base nesta e FWC: s resposta que criou um pacote instalável pip https: // github .com / mkorpela / substituições
De vez em quando eu acabar aqui a olhar para esta questão. Principalmente isso acontece depois (novamente) vendo o mesmo bug na nossa base de código: Alguém se esqueceu de alguma "interface" implementação de classe, enquanto renomeando um método na "interface" ..
Bem Python não é Java, mas Python tem poder - e explícito é melhor do que implícito -. E há casos concretos reais no mundo real, onde essa coisa teria me ajudaram
Então aqui está um esboço de substituições decorador. Este irá verificar que a classe dado como um parâmetro tem o mesmo método (ou algo assim) nome que o método que está sendo decorado.
Se você pode pensar em uma solução melhor por favor poste aqui!
def overrides(interface_class):
def overrider(method):
assert(method.__name__ in dir(interface_class))
return method
return overrider
Ele funciona da seguinte forma:
class MySuperInterface(object):
def my_method(self):
print 'hello world!'
class ConcreteImplementer(MySuperInterface):
@overrides(MySuperInterface)
def my_method(self):
print 'hello kitty!'
e se você fizer uma versão defeituosa que irá aumentar um erro de declaração durante o carregamento de classe:
class ConcreteFaultyImplementer(MySuperInterface):
@overrides(MySuperInterface)
def your_method(self):
print 'bye bye!'
>> AssertionError!!!!!!!
Outras dicas
Aqui está uma implementação que não requer especificação do nome 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
Se você quer este apenas para fins de documentação, você pode definir seu próprio decorador de substituição:
def override(f):
return f
class MyClass (BaseClass):
@override
def method(self):
pass
Este é realmente nada, mas olho-doce, a menos que você criar override (f) de tal forma que é, na verdade, verifica a existência de uma substituição.
Mas então, este é Python, por que escrever como se fosse Java?
Python não é Java. Há, claro, há tal coisa realmente como a verificação em tempo de compilação.
Eu acho que um comentário no docstring é suficiente. Isso permite que qualquer usuário do seu método de tipo help(obj.method)
e ver que o método é uma substituição.
Você também pode estender explicitamente uma interface com class Foo(Interface)
, o que permitirá que os usuários digitem help(Interface.method)
para ter uma idéia sobre a funcionalidade de seu método se destina a fornecer.
Como os outros disseram que ao contrário Java não há tag @Overide no entanto acima, você pode criar seus próprios usando decoradores no entanto, sugiro usar o getattrib () método global em vez de usar o dict interna assim que você começa algo como o seguinte:
def Override(superClass):
def method(func)
getattr(superClass,method.__name__)
return method
Se você quiser você pode pegar getattr () em seu próprio aumento try catch seu próprio erro, mas eu acho método getattr é melhor neste caso.
Além disso, este pega todos os itens vinculados a uma classe, incluindo métodos de classe e vairables
Improvisação no @mkorpela grande resposta , aqui está uma versão com
verificações mais precisas, nomeando, e levantou Erro objetos
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
Aqui está o que parece na prática:
NotImplementedError
" não implementado na classe base "
class A(object):
# ERROR: `a` is not a implemented!
pass
class B(A):
@overrides(A)
def a(self):
pass
resulta em erro NotImplementedError
mais descritivo
function "a" is an @override but that function is not implemented in base class <class '__main__.A'>
pilha completa ??p>
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
" esperado tipo implementado "
class A(object):
# ERROR: `a` is not a function!
a = ''
class B(A):
@overrides(A)
def a(self):
pass
resulta em erro NotImplementedError
mais descritivo
function "a" is an @override but that is implemented as type <class 'str'> in base class <class '__main__.A'>, expected implemented type <class 'function'>
pilha completa ??p>
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'>
A grande coisa sobre @mkorpela resposta é a verificação acontece durante alguns fase de inicialização. A seleção não precisa ser "run". Referindo-se aos exemplos anteriores, class B
nunca é inicializado (B()
) ainda a NotImplementedError
ainda vai aumentar. Isto significa overrides
erros são capturados mais cedo.
Com base em grande resposta de @ mkorpela, eu escrevi um pacote semelhante ( iPromise pypi github ) que faz muito mais verificações:
A herda Suponha de B e C. e herda B de C. iPromise cheques que
-
Se A.F substituições B.f, B.f deve existir, e deve herdar de B. (Este é o cheque do pacote substituições).
-
Você não tem o padrão Af declara que ele substitui Bf, que, em seguida, declara que ele substitui Cf A deve dizer que ela substitui a partir de Cf desde B pode decidir parar de substituir esse método, e isso não deve resultar em atualizações a jusante.
-
Você não tem o padrão A.F declara que ele substitui C.f, mas não B.f não declarar a sua substituição.
-
Você não tem o padrão A.F declara que ele substitui C.f, mas B.f declara que ele substitui a partir de alguns d.f.
Ele também tem várias características de marcação e verificação de implementação de um método abstrato.
Hear é mais simples e trabalhando sob Jython com classes Java:
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()