问题
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?
回答1:
UPDATE (23.05.2015): 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!!!!!!!
回答2:
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
回答3:
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?
回答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.
回答5:
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
回答6:
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.
回答7:
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
overridesB.f
,B.f
must exist, andA
must inherit fromB
. (This is the check from the overrides package).You don't have the pattern
A.f
declares that it overridesB.f
, which then declares that it overridesC.f
.A
should say that it overrides fromC.f
sinceB
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 overridesC.f
, butB.f
does not declare its override.You don't have the pattern
A.f
declares that it overridesC.f
, butB.f
declares that it overrides from someD.f
.
It also has various features for marking and checking implementing an abstract method.
回答8:
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()
回答9:
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.
来源:https://stackoverflow.com/questions/1167617/in-python-how-do-i-indicate-im-overriding-a-method