Вопрос

В Java, например, @Override аннотация не только обеспечивает проверку переопределения во время компиляции, но и создает превосходный самодокументируемый код.

Я просто ищу документацию (хотя, если это индикатор для какой-нибудь проверки, такой как pylint, это бонус).Я могу добавить комментарий или строку документа где-нибудь, но каков идиоматический способ указать переопределение в Python?

Это было полезно?

Решение

ОБНОВЛЕНИЕ (23.05.2015):Основываясь на этом и ответе fwc: s, я создал устанавливаемый пакет pip https://github.com/mkorpela/overrides

Время от времени я оказываюсь здесь, рассматривая этот вопрос.В основном это происходит после того, как (снова) мы видим ту же ошибку в нашей кодовой базе:Кто-то забыл какой-то класс, реализующий "интерфейс", при переименовании метода в "интерфейсе"..

Ну, Python - это не Java, но у Python есть мощь - и явное лучше неявного - и есть реальные конкретные случаи в реальном мире, где эта штука помогла бы мне.

Итак, вот эскиз переопределения декоратора.Это проверит, что класс, указанный в качестве параметра, имеет то же имя метода (или что-то в этом роде), что и оформляемый метод.

Если вы можете придумать лучшее решение, пожалуйста, разместите его здесь!

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) таким образом, который фактически проверяет наличие переопределения.

Но тогда, это Python, зачем писать его так, как будто это была Java?

Python - это не Java.Конечно, на самом деле такой вещи, как проверка во время компиляции, не существует.

Я думаю, что комментария в строке документа вполне достаточно.Это позволяет любому пользователю вашего метода вводить help(obj.method) и убедитесь, что метод является переопределенным.

Вы также можете явно расширить интерфейс с помощью class Foo(Interface), который позволит пользователям вводить help(Interface.method) чтобы получить представление о функциональности, которую призван обеспечить ваш метод.

Как уже говорили другие, в отличие от Java, здесь нет тега @Overide, однако выше вы можете создать свой собственный, используя декораторы, однако я бы предложил использовать глобальный метод getattrib() вместо использования внутреннего dict, чтобы вы получили что-то вроде следующего:

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

Если бы вы захотели, вы могли бы поймать getattr() в своей собственной попытке catch вызвать вашу собственную ошибку, но я думаю, что метод getattr в этом случае лучше.

Кроме того, это улавливает все элементы, привязанные к классу, включая методы класса и vairables

Импровизирую на @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 ошибки выявляются раньше.

Основываясь на замечательном ответе @mkorpela, я написал аналогичный пакет (я обещаю pypi ( пипи ) гитхаб) , который выполняет еще много проверок:

Предположим , что 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 .

Он также имеет различные функции для маркировки и проверки реализации абстрактного метода.

Hear является самым простым и работает под управлением Jython с классами 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()
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top