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?

Foi útil?

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

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

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()
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top