In Python, how do I indicate I'm overriding a method?
Asked Answered
M

14

266

In Java, for example, the @Override annotation not only provides compile-time checking of an override but makes for excellent self-documenting code.

I'm just looking for documentation (although if it's an indicator to some checker like pylint, that's a bonus). I can add a comment or docstring somewhere, but what is the idiomatic way to indicate an override in Python?

Malley answered 22/7, 2009 at 19:26 Comment(8)
In other words, you don't ever indicate that you're overriding a method? Leave it to the reader to figure that out himself?Malley
Yeah, I know it seems like an error prone situation coming from a compiled language, but you just have to accept it. In practice I have not found it to be much of a problem (Ruby in my case, not Python, but same idea)Sectionalize
Sure, done. Both Triptych's answer and mkorpela's answers are simple, I like that, but the latter's explicit-over-implicit spirit, and intelligibly preventing mistakes wins.Malley
It's not directly the same, but abstract base classes check if all abstract methods have been overridden by a subclass. Of course this doesn't help if you're overriding concrete methods.Cremator
See PEP 698 peps.python.org/pep-0698, an @override decorator have been added in Python 3.12 and can be imported from typing e.g. from typing import override docs.python.org/3.12/library/typing.html#typing.overridePrecaution
@YogevNeumann that sounds like it should be its own answer. Check out this answer.Malley
Oh, I wish it wasn't that low :( consider changing the accepted answer to @Lorenzo answerPrecaution
I will consider it for sure! I'm excited for official support. For a while, I don't think the answer will be viable for most users, as Python 3.12 is still alpha, and the version will take a while to gain usage share.Malley
L
272

Based on this and fwc:s answer I created a pip installable package https://github.com/mkorpela/overrides

From time to time I end up here looking at this question. Mainly this happens after (again) seeing the same bug in our code base: Someone has forgotten some "interface" implementing class while renaming a method in the "interface"..

Well Python ain't Java but Python has power -- and explicit is better than implicit -- and there are real concrete cases in the real world where this thing would have helped me.

So here is a sketch of overrides decorator. This will check that the class given as a parameter has the same method (or something) name as the method being decorated.

If you can think of a better solution please post it here!

def overrides(interface_class):
    def overrider(method):
        assert(method.__name__ in dir(interface_class))
        return method
    return overrider

It works as follows:

class MySuperInterface(object):
    def my_method(self):
        print 'hello world!'


class ConcreteImplementer(MySuperInterface):
    @overrides(MySuperInterface)
    def my_method(self):
        print 'hello kitty!'

and if you do a faulty version it will raise an assertion error during class loading:

class ConcreteFaultyImplementer(MySuperInterface):
    @overrides(MySuperInterface)
    def your_method(self):
        print 'bye bye!'

>> AssertionError!!!!!!!
Lox answered 29/11, 2011 at 15:7 Comment(12)
Awesome. This caught a misspelling bug the first time I tried it. Kudos.Hagridden
So, I am using python 2.7 and if my class extends a bunch of other classes and, unlike with the interface, I do not want to hard-code the exact class name that contains the interface function, then can this work in general if I inherit from more than one class or will the method resolution order break this?Kaolin
How would you write an assertion that checks whether the number of arguments is the same in the base class/interface and the child class?Uvarovite
I like your decorator, but its too bad that the assertion has to be called every time the function is executed. Might there be some way of getting around this using metaclasses?Granite
mfbutner: it is not called every time the method is executed - only when the method is created.Lox
This is also nice for doc strings! overrides could copy the docstring of the overridden method if the overriding method doesn't have one of its own.Cremator
@mkorpela, Heh this code of yours should be in python default lib system. Why you don't put this in pip system? :PSian
@urosjarc: great idea! I thought it might be too little code for a pip package but why not.Lox
@Lox : Oh and I suggest to inform the python core developers obout this package, they might want to consider obout adding overide decorator to the core python system. :)Sian
If the overridden method is marked with @abstractmethod, then you could also check for an identical parameter list.Antoine
To note: the Python package is quite more sophisticated than the original sketch, and no longer requires re-specifying the base class.Secrecy
@Lox I just added your library to my project and it works perfect! Thanks and job well done.Photodrama
W
41

As of python 3.12 (release date fall 2023) this can be done. I would suggest you to look at this website https://peps.python.org/pep-0698/. It explains really well how to decorate methods in Python like in Java.

Here a code example, for more details you can check out the website above.

from typing import override

class Parent:
    def foo(self) -> int:
        return 1

    def bar(self, x: str) -> str:
        return x

class Child(Parent):
    @override
    def foo(self) -> int:
        return 2

    @override
    def baz() -> int:  # Type check error: no matching signature in ancestor
        return 1
Wardle answered 21/3, 2023 at 14:0 Comment(2)
Additional references: docs.python.org/3.12/whatsnew/3.12.html#typing and github.com/python/cpython/pull/101564Ruffina
There's a backport in typing_extensions for older Python versionsShoveler
C
31

Here's an implementation that doesn't require specification of the interface_class name.

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
Communicate answered 31/1, 2013 at 17:13 Comment(5)
A little magical but makes typical usage a lot easier. Can you include usage examples?Malley
what are the average and worst-case costs of using this decorator, perhaps expressed as a comparison with a build-in decorator like @classmethod or @property?Evolute
@Evolute This decorator is executed once, when class definition is analyzed, not on each call. Therefore it's execution cost is irrelevant, when compared to program runtime.Apparitor
This will be much nicer in Python 3.6 thanks to PEP 487.Antoine
To get better error message: assert any(hasattr(cls, method.__name__) for cls in base_classes), 'Overriden method "{}" was not found in the base class.'.format(method.__name__)Abstract
A
24

If you want this for documentation purposes only, you can define your own override decorator:

def override(f):
    return f


class MyClass (BaseClass):

    @override
    def method(self):
        pass

This is really nothing but eye-candy, unless you create override(f) in such a way that is actually checks for an override.

But then, this is Python, why write it like it was Java?

Aptitude answered 22/7, 2009 at 22:33 Comment(4)
One could add actual validation via inspection to the override decorator.Rusert
But then, this is Python, why write it like it was Java? Because some ideas in Java are good and worth extending to other languages?Satrap
Because when you rename a method in a superclass it would be nice to know that some subclass 2 levels down was overriding it. Sure, it's easy to check, but a little help from language parser wouldn't hurt.Apparitor
Because it is a good idea. The fact that a variety of other languages have the feature is no argument - either for or against.Cuprum
L
7

Improvising on @mkorpela great answer, here is a version with

more precise checks, naming, and raised Error objects

def overrides(interface_class):
    """
    Function override annotation.
    Corollary to @abc.abstractmethod where the override is not of an
    abstractmethod.
    Modified from answer https://mcmap.net/q/108831/-in-python-how-do-i-indicate-i-39-m-overriding-a-method
    """
    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


Here is what it looks like in practice:

NotImplementedError "not implemented in base class"

class A(object):
    # ERROR: `a` is not a implemented!
    pass

class B(A):
    @overrides(A)
    def a(self):
        pass

results in more descriptive NotImplementedError error

function "a" is an @override but that function is not implemented in base class <class '__main__.A'>

full stack

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 "expected implemented type"

class A(object):
    # ERROR: `a` is not a function!
    a = ''

class B(A):
    @overrides(A)
    def a(self):
        pass

results in more descriptive NotImplementedError error

function "a" is an @override but that is implemented as type <class 'str'> in base class <class '__main__.A'>, expected implemented type <class 'function'>

full stack

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'>




The great thing about @mkorpela answer is the check happens during some initialization phase. The check does not need to be "run". Referring to the prior examples, class B is never initialized (B()) yet the NotImplementedError will still raise. This means overrides errors are caught sooner.

Lazarus answered 5/2, 2019 at 1:37 Comment(3)
Hey! This looks interesting. Could you consider doing a pull request on my ipromise project? I've added an answer.Antoine
@NeilG I forked the ipromise project and coded a bit. It seems like you've essentially implemented this within overrides.py. I'm not sure what else I can significantly improve except to change the exception types from TypeError to NotImplementedError.Lazarus
Hey! Thanks, I don't have checks that the overridden object actually has type types.MethodType. That was a good idea in your answer.Antoine
O
4

Python ain't Java. There's of course no such thing really as compile-time checking.

I think a comment in the docstring is plenty. This allows any user of your method to type help(obj.method) and see that the method is an override.

You can also explicitly extend an interface with class Foo(Interface), which will allow users to type help(Interface.method) to get an idea about the functionality your method is intended to provide.

Otherwise answered 22/7, 2009 at 19:32 Comment(4)
The real point of @Override in Java isn't to document - it's to catch a mistake when you intended to override a method, but ended up defining a new one (e.g. because you misspelled a name; in Java, it may also happen because you used the wrong signature, but this isn't an issue in Python - but spelling mistake still is).Motherwell
@ Pavel Minaev: True, but it is still convenient to have for documentation, especially if you're using an IDE / text editor which doesn't have automatic indicators for overrides (Eclipse's JDT shows them neatly alongside line numbers, for example).Sandarac
@PavelMinaev Wrong. One of the main points of @Override is documentation in addition to compile time checking.Boston
@Boston I think an aid to documentation is great, but in all the official Java documentation I see, they only indicate the importance of the compile time checks. Please substantiate your claim that Pavel is "wrong."Butch
A
2

Like others have said unlike Java there is not @Overide tag however above you can create your own using decorators however I would suggest using the getattrib() global method instead of using the internal dict so you get something like the following:

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

If you wanted to you could catch getattr() in your own try catch raise your own error but I think getattr method is better in this case.

Also this catches all items bound to a class including class methods and vairables

Acetophenetidin answered 12/8, 2013 at 17:43 Comment(0)
A
2

Based on @mkorpela's great answer, I've written a similar package (ipromise pypi github) that does many more checks:

Suppose A inherits from B and C, B inherits from C.

Module ipromise checks that:

  • If A.f overrides B.f, B.f must exist, and A must inherit from B. (This is the check from the overrides package).

  • You don't have the pattern A.f declares that it overrides B.f, which then declares that it overrides C.f. A should say that it overrides from C.f since B might decide to stop overriding this method, and that should not result in downstream updates.

  • You don't have the pattern A.f declares that it overrides C.f, but B.f does not declare its override.

  • You don't have the pattern A.f declares that it overrides C.f, but B.f declares that it overrides from some D.f.

It also has various features for marking and checking implementing an abstract method.

Antoine answered 14/3, 2019 at 0:3 Comment(0)
K
1

You can use protocols from PEP 544. With this method, the interface-implementation relation is declared only at the use site.

Assuming you already have an implementation (let's call it MyFoobar), you define an interface (a Protocol), which has the signatures of all the methods and fields of your implementation, let's call that IFoobar.

Then, at the use site, you declare the implementation instance binding to have the interface type e.g. myFoobar: IFoobar = MyFoobar(). Now, if you use a field/method that is missing in the interface, Mypy will complain at the use site (even if it would work at runtime!). If you failed to implement a method from the interface in the implementation, Mypy will also complain. Mypy won't complain if you implement something that doesn't exist in the interface. But that case is rare, since the interface definition is compact and easy to review. You wouldn't be able to actually use that code, since Mypy would complain.

Now, this won't cover cases where you have implementations both in the superclass and the implementing class, like some uses of ABC. But override is used in Java even with no implementation in the interface. This solution covers that case.

from typing import Protocol

class A(Protocol):
    def b(self):
        ...
    def d(self):  # we forgot to implement this in C
        ...

class C:
    def b(self):
        return 0

bob: A = C()

Type checking results in:

test.py:13: error: Incompatible types in assignment (expression has type "C", variable has type "A")
test.py:13: note: 'C' is missing following 'A' protocol member:
test.py:13: note:     d
Found 1 error in 1 file (checked 1 source file)
Kameko answered 22/1, 2021 at 17:22 Comment(4)
Got an example? Something that would pass/fail mypy?Malley
@Bluu: Yes, look at the section defining a protocolKameko
Can you include example source code and mypy results in your answer? And required version number and tools (I think mypy is new to this question thread)? I think that would make your answer more self-contained.Malley
@Malley I have added it.Kameko
K
1

as python 3.6 and above, the functionality provided by @override can be easily implemented using the descriptor protocol of python, namingly the set_name dunder method:

class override:
    def __init__(self, func):
       self._func = func
       update_wrapper(self, func)

    def __get__(self, obj, obj_type):
        if obj is None:
            return self
        return self._func

    def __set_name__(self, obj_type, name):
        self.validate_override(obj_type, name)

    def validate_override(self, obj_type, name):
        for parent in obj_type.__bases__:
            func = parent.__dict__.get(name, None)
            if callable(func):
                return
        else:
            raise NotImplementedError(f"{obj_type.__name__} does not override {name}")

Note that here set_name is called once the wrapped class is defined, and we can get the parent class of the wrapped class by calling its dunder method bases.

for each for its parent class, we would like to check if the wrapped function is implemented in the class by

  1. check that the function name is in the class dict
  2. it is a callable

Using i would be as simple as:

class AbstractShoppingCartService:
    def add_item(self, request: AddItemRequest) -> Cart:
        ...


class ShoppingCartService(AbstractShoppingCartService):
    @override
    def add_item(self, request: AddItemRequest) -> Cart:
        ...
Kalynkam answered 11/5, 2022 at 8:7 Comment(1)
Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.Expecting
S
0

Not only did the decorator I made check if the name of the overriding attribute in is any superclass of the class the attribute is in without having to specify a superclass, this decorator also check to ensure the overriding attribute must be the same type as the overridden attribute. Class Methods are treated like methods and Static Methods are treated like functions. This decorator works for callables, class methods, static methods, and properties.

For source code see: https://github.com/fireuser909/override

This decorator only works for classes that are instances of override.OverridesMeta but if your class is an instance of a custom metaclass use the create_custom_overrides_meta function to create a metaclass that is compatible with the override decorator. For tests, run the override.__init__ module.

Sidewalk answered 24/9, 2019 at 15:30 Comment(0)
S
0

In Python 2.6+ and Python 3.2+ you can do it (Actually simulate it, Python doesn't support function overloading and child class automatically overrides parent's method). We can use Decorators for this. But first, note that Python's @decorators and Java's @Annotations are totally different things. The prior one is a wrapper with concrete code while later one is a flag to compiler.

For this, first do 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)

output:

foo in A
foo in B
foo in B; arg = 4
foo in B; arg = ('Wheee', 3.14)

Note that you must have to use decorator here with parenthesis

One thing to remember is that since Python doesn't have function overloading directly, so even if Class B don't inherit from Class A but needs all those foos than also you need to use @Override (though using alias 'Overload' will look better in that case)

Scottyscotus answered 5/7, 2020 at 21:11 Comment(0)
K
0

Here is a different solution without annotation.

It has a slightly other goal in mind. While the other proposed solutions check if the given method actually overrides a parent, this one checks, if all parent methods were overridden.

You don't have to raise an AssertionError, but can print a warning or disable it in production by checking for the env in __init__ and return before checking.

class Parent:

    def a():
        pass

    def b():
        pass

class Child(Overrides, Parent):

    def a()

    # raises an error, as b() is not overridden


class Overrides:

    def __init__(self):
        # collect all defined methods of all base-classes
        bases = [b for b in self.__class__.__bases__ if b != Overrides]
        required_methods = set()
        for base in bases:
            required_methods = required_methods.union(set([f for f in dir(base) if not f.startswith('_')]))
        
        # check for each method in each base class (in required_methods)
        # if the class, that inherits `Overrides` implements them all
        missing = []
        # me is the fully qualified name of the CLASS, which inherits 
        # `Overrides`
        me = self.__class__.__qualname__
        for required_method in required_methods:

            # The method can be either defined in the parent or the child 
            # class. To check it, we get a reference to the method via 
            # getattr
            try:
                found = getattr(self, required_method)
            except AttributeError:
                # this should not happen, as getattr returns the method in 
                # the parent class if it is not defined in the cild class.
                # It has to be in a parent class, as the required_methods 
                # is a union of all base-class methods.
                missing.append(required_method)
                continue
            
            # here is, where the magic happens.
            # found is a reference to a method, and found.__qualname__ is
            # the full-name of the METHOD. Remember, that me is the full
            # name of the class. 
            # We want to check, where the method is defined. If it is 
            # defined in an parent class, we did no override it, thus it 
            # is missing. 
            # If we did not override, the __qualname__ is Parent.method
            # If we did override it, the __qualname__ is Child.method
            # With this fact, we can determine if the class, which uses
            # `Override` did implement it.
            if not found.__qualname__.startswith(me + '.'):
                missing.append(required_method)

        # Maybe a warning would be enough here
        if missing != []:
            raise AssertionError(f'{me} did not override these methods: {missing}')
Kickstand answered 18/9, 2022 at 15:24 Comment(0)
L
-1

Hear is simplest and working under Jython with Java classes:

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()
Li answered 25/11, 2013 at 21:2 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.