Reputation: 6189
I have the following two decorators:
Decorator 1:
import functools
def overrides(interface_class):
print('interface_class:', interface_class.__name__)
def decorator(method):
print('method:', method.__name__)
@functools.wraps(method)
def func(*args, **kwargs):
assert (method.__name__ in dir(interface_class))
return method
return func
return decorator
Decorator 2:
def overrides(interface_class):
def overrider(method):
print('Method name:', method.__name__)
assert(method.__name__ in dir(interface_class))
return method
return overrider
For decorator 1 I rewrote an example, which is using functools. Decorator 2 I found as a working example on Stackoverflow. Decorator 1 is not executing the assert statement, while decorator 2 does, whenever I run the containing program. What is the difference between these two, which causes this?
EDIT#1:
Following the answer of this question, the first comment and another example, I've rewritten it and it works now:
def overrides(interface_class):
def my_decorator(method):
assert (method.__name__ in dir(interface_class)), \
'Trying to override a method named ' + \
method.__name__ + \
' in the interface ' + \
interface_class.__name__ + \
', which does not exist in the interface. Is this a method naming issue?'
@functools.wraps(method)
def wrapped(*args, **kwargs):
return method(*args, **kwargs)
return wrapped
return my_decorator
I'll try to explain the placement of the assert
statement in my own words: Since the assert
statement is not part of the "new functionality" of the method (in fact there is no new functionality, but only assertions about it), it does not belong inside of the function func
, but outside of it. It'll only be executed once, when the method is decorated, which will happen, when the program is started, because that's the time, when function decorators are applied, while it would be executed everytime the decorated function is called, if it was within that decorated function. Running the assertion everytime is not desirable, because of its nature.
– Is there still any mistake in this explanation?
Upvotes: 1
Views: 108
Reputation: 104712
Your first decorator attempts to do the check that the method is part of the interface when the method is called. The second version does the check when the decorator is run (at method definition time).
There's an error in your first version, which may be preventing you from seeing it work. The inner most function is returning method
, when it should be calling it and returning the result: return method(*args, **kwargs)
The functools.wraps
function is useful in the first decorator because you're replacing the original method with a wrapper. The wraps
decorator makes it so the name and docstring of the wrapper function match those of the wrapped function (so that things like help
work as expected). The second decorator doesn't replace the method at all (the decorator returns it unchanged if the assertion is not hit), so there's no wrapper function that needs functools.wraps
's help.
Upvotes: 1