В Python, как я могу указать, что я переопределяю метод?

Например, в @Override аннотация @Override не только обеспечивает проверку переопределения во время компиляции, но и обеспечивает отличный самодокументирующийся код.

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

Ответ 1

ОБНОВЛЕНИЕ (23.05.2015): Основываясь на этом и ответе fwc: s, я создал пакет с возможностью установки 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!!!!!!!

Ответ 2

Здесь реализована реализация, которая не требует спецификации имени 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

Ответ 3

Если вы хотите это только для целей документирования, вы можете определить свой собственный переопределяющий декоратор:

def override(f):
    return f


class MyClass (BaseClass):

    @override
    def method(self):
        pass

Это действительно ничего, кроме глазных конфет, если вы не создадите переопределение (f) таким образом, чтобы на самом деле проверять переопределение.

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

Ответ 4

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

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

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

Ответ 5

Насколько я знаю, нет специального способа указать переопределение в Python. Вы просто определяете метод и включаете docstring, как всегда.

Ответ 6

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

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

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

Также это захватывает все элементы, связанные с классом, включая методы класса и vairables

Ответ 7

Импровизация на @mkorpela отличный ответ, вот версия с

более точные проверки, присвоение имен и поднятие объектов Error

def overrides(interface_class):
    """
    Function override annotation.
    Corollary to @abc.abstractmethod where the override is not of an
    abstractmethod.
    Modified from answer /questions/64375/in-python-how-do-i-indicate-im-overriding-a-method/441969#441969
    """
    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 обнаруживаются раньше.

Ответ 8

Основываясь на замечательном ответе @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.

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

Ответ 9

Слушание прост и работает под 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()

Ответ 10

Мало того, что декоратор, который я сделал, проверял, является ли имя переопределяющего атрибута каким-либо суперклассом класса, в котором находится атрибут, без необходимости указывать суперкласс, этот декоратор также проверял, чтобы атрибут переопределения был того же типа, что и переопределенный приписывать. Методы класса обрабатываются как методы, а статические методы - как функции. Этот декоратор работает с вызываемыми объектами, методами классов, статическими методами и свойствами.

Исходный код см.: https://github.com/fireuser909/override

Этот декоратор работает только для классов, которые являются экземплярами override.OverridesMeta, но если ваш класс является экземпляром пользовательского метакласса, используйте функцию create_custom_overrides_meta для создания метакласса, совместимого с декоратором переопределения. Для тестов запустите модуль переопределения.__ init__.