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

前端 未结 10 1867
长情又很酷
长情又很酷 2020-11-29 15:43

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

I\

相关标签:
10条回答
  • 2020-11-29 16:37

    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?

    0 讨论(0)
  • 2020-11-29 16:42

    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://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
    


    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.

    0 讨论(0)
  • 2020-11-29 16:44

    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)

    0 讨论(0)
  • 2020-11-29 16:49

    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!!!!!!!
    
    0 讨论(0)
提交回复
热议问题