@decorator
def pass_decorator(f, *args, **kw): # what about the slow-down that
incurs when using pass_decorator?
return f(*args, **kw)
@decorator
def trace_decorator(f, *args, **kw):
print "calling %s with args %s, %s" % (f.func_name, args, kw)
return f(*args, **kw)
trace_enable_flag = False #__debug__
trace = trace_decorator if trace_enable_flag else pass_decorator
Trouble is, there is still an additional encapsulating function call.
Is there any way to eliminate the extra function call altogether?
Thanks in advance!
I think you have misunderstood certain details about decorators.
This code with a decorator:
@decorator
def hello():
print "hello world"
basically does this:
def hello():
print "hello world"
hello = decorator(hello)
so your non-decorator just needs to return the function it was passed:
def pass_decorator(func):
return func
and your trace decorator would be something like this:
def trace_decorator(func):
def show(*args, **kwargs):
print "calling %s with args %s, %s" % (func.func_name,
args, kwargs)
result = func(*args, **kwargs)
print "returning %s from %s" % (result, func.func_name)
return result
return show
> Hi all, I have a question about decorators. I would like to use them for
> argument checking, and pre/post conditions. However, I don't want the
> additional overhead when I run in non-debug mode. I could do something
> like this, using a simple trace example.
def decorator(func):
if __debug__:
@functools.wraps(func)
def inner(*args, **kwargs):
preprocess()
result = func(*args, **kwargs)
postprocess()
return result
return inner
else:
return func
--
Steven
Sure, but during runtime, pass_decorator will still be called, even
though it doesn't do anything. I am wondering if I can disable this
additional function call at non-debug runtime. In other words, I want
the calling stack to contain no decorator at all, not even
pass_decorator.
I should mention that, in my code, the symbol "decorator" is imported
from Michele Simionato's decorator.py file.
Actually, I am using "decorator" from Michele Simionato's decorator.py
file, at http://pypi.python.org/pypi/decorator.
pass_decorator will be called when the decorated function is _defined_,
but not when the decorated function is _called_.
Why is it then that during runtime, with a breakpoint in some
arbitrary main() in main.py, I get something similar to the following
call stack:
main.py, line xxx, in <module>
main()
<string>, line 2, in main
decorator.py, line 261, in pass_decorator
return f(*args, **kw)
main.py, line yyy, in main()
* breakpoint line here *
It looks to me the call stack still includes the additional level of
the decorator... what am I missing? Thank you for your time.
Are you still defining your decorators in the same way as in your
original post?
A decorator shouldn't call the function it's decorating.
My version of pass_decorator just returns the function it's decorating,
not call it, and my version of trace_decorator returns a locally-defined
function which will call the decorated function.
You're not defining pass_decorator correctly. Always show us the actual
code when you're showing results.
Your pass_decorator should just return the original function,
undecorated, unmodified if you're not in debug. It should only return a
decorated function otherwise.
Consider:
-----
from decorator import decorator
def debug_decorator(fn):
if __debug__:
def wrapper(fn, *args, **kwargs):
# insert pre-condition testing here
if len(args) != 1:
raise ValueError("Incorrect number of arguments")
result = fn(*args, **kwargs)
# insert post-condition testing here
if result not in (True, False):
raise ValueError("Incorrect return value!")
return result
return decorator(wrapper, fn)
else:
return fn
@debug_decorator
def my_test(arg):
if not arg:
raise RuntimeError
return True
my_test(0)
-----
And the output depending on if you're in debug mode or not:
Top:test ixokai$ python deco.py
Traceback (most recent call last):
File "deco.py", line 27, in <module>
my_test(0)
File "<string>", line 2, in my_test
File "deco.py", line 10, in wrapper
result = fn(*args, **kwargs)
File "deco.py", line 24, in my_test
raise RuntimeError
RuntimeError
Top:test ixokai$ python -O deco.py
Traceback (most recent call last):
File "deco.py", line 27, in <module>
my_test(0)
File "deco.py", line 24, in my_test
raise RuntimeError
RuntimeError
--
--S
... p.s: change the ".invalid" to ".com" in email address to reply privately.
> LX wrote:
[...]
>> It looks to me the call stack still includes the additional level of
>> the decorator... what am I missing? Thank you for your time.
>
> Are you still defining your decorators in the same way as in your
> original post?
>
> A decorator shouldn't call the function it's decorating.
*raises eyebrow*
Surely, in the general case, a decorator SHOULD call the function it is
decorating? I'm sure you know that, but your wording is funny and could
confuse the OP.
In this specific case, where the OP wants a "do nothing pass-decorator",
he should do this:
def decorator(func):
if __debug__:
...
else:
return func
rather than this:
def decorator(func):
if __debug__:
...
else:
def inner(*args):
return func(*args)
return inner
--
Steven
For example, in my version of trace_decorator() it's show() that calls
the decorated function, not trace_decorator() itself.
Unless the word 'decorator' refers to the locally-defined function, in
which case, what do you call the function that does the wrapping, the
one whose name follows the '@'?
Thank you all a lot! The last 3 posts helped me figure it out. Indeed,
my pass_decorator function used "@decorator", using the decoratory.py
library. This prevented return of the original function. The code that
I have now is:
#NOT THIS:
#@decorator
#def pass_decorator(f, *args, **kw): # what about the slow-down that
incurs when using pass_decorator
#return f(*args, **kw)
def pass_decorator(f):
return f
>>> A decorator shouldn't call the function it's decorating.
>>
>> *raises eyebrow*
>>
>> Surely, in the general case, a decorator SHOULD call the function it is
>> decorating? I'm sure you know that, but your wording is funny and could
>> confuse the OP.
>>
> What I mean is that the function that's doing the decorating shouldn't
> call the function; it's the locally-defined wrapper function that calls
> the decorated function.
Ah, gotcha, that makes sense. Now I understand the distinction you were
making. Thank you for the clarification.
--
Steven
It would make more sense (to me, at least) if the decoratee were the
function passed as an argument to the decorator.
regards
Steve
--
Steve Holden +1 571 484 6266 +1 800 494 3119
See PyCon Talks from Atlanta 2010 http://pycon.blip.tv/
Holden Web LLC http://www.holdenweb.com/
UPCOMING EVENTS: http://holdenweb.eventbrite.com/
Me too. I do like the idea of coming up with a consistent terminology.