decorators as generalized pre-binding hooks

Bengt Richter bokr at oz.net
Sun Jul 10 23:03:57 CEST 2005


On Sun, 10 Jul 2005 05:35:01 GMT, Ron Adam <rrr at ronadam.com> wrote:

>Bengt Richter wrote:
>> ;-)
>> We have
>
>Have we?
>
>Looks like not a lot of interested takers so far.
>
>But I'll bite. ;-)
>
>
><clip intro>
>
>> So why not
>> 
>>     @deco
>>     foo = lambda:pass
>> equivalent to
>>     foo = deco(lambda:pass)
> >
>> and from there,
>>     @deco
>>     <left-hand-side> = <right-hand-side>
>> being equivalent to
>>     <left-hand-side> = deco(<right-hand-side>)
>> 
>> e.g.,
>>     @range_check(1,5)
>>     a = 42
>> for
>>     a = range_check(1,5)(42)
>> 
>> or
>>     @default_value(42) 
>>     b = c.e['f']('g')
>> for
>>     b = default_value(42)(c.e['f']('g'))
>
>So far they are fairly equivalent.  So there's not really any advantage 
>over the equivalent inline function.  But I think I see what you are 
>going towards.  Decorators currently must be used when a function is 
>defined.  This option attempts to makes them more dynamic so that they 
>can be used where and when they are needed.
IMO part of the decorator benefit is clearer code, and also IMO the @range_check
and @default_value decorators succeed in that. The code generated would presumably
be the same, unless the exception capture discussed further down were implemented.

>
>How about if you make it optional too?
>
>if keep_log:
>    @log_of_interesting_values
>b = get_value(c,d):
>
I'd probably pass the condition to the decorator for a less ragged look
    @log_of_interesting_values(keep_log)
    b = get_value(c,d)
>Or...
>
>@@keeplog log_of_interesting_values     # if keeplog decorate.
>b = get_value(c,d)
>
>Just a thought.
Yes, but that is too easy to do another way. Plus I want to reserve '@@' for an AST-time
decoration idea I have ;-)

>
>> Hm, binding-intercept-decoration could be sugar for catching exceptions too,
>> and passing them to the decorator, e.g., the above could be sugar for
>> 
>>     try:
>>         b = default_value(42)(c.e['f']('g'))
>>     except Exception, e:
>>         b = default_value(__exception__=e) # so decorator can check
>>                                # and either return a value or just re-raise with raise [Note 1]
>
>I'm not sure I follow this one.. Well I do a little. Looks like it might 
>be going the direction of with statements, but only applied to a single 
>expression instead of a block or suite.

Yes, it's just expanding on the idea of intercepting the value of the object being bound even
if the creation of that object resulted in an exception, and allowing the decorator to handle
it either way.
>
>
>> This might be useful for plain old function decorators too, if you wanted the decorator
>> to define the policy for substituting something if e.g. a default argument evaluation
>> throws and exception. Thus
>> 
>>     @deco
>>     def foo(x=a/b): pass # e.g., what if b==0?
>> as
>>     try:
>>         def foo(x=a/b): pass # e.g., what if b==0?
>>         foo = deco(foo)
>>     except Exception, e:
>>         if not deco.func_code.co_flags&0x08: raise #avoid mysterious unexpected keyword TypeError
>>         foo = deco(__exception__=e)
>
>Wouldn't this one work now?
I think all the second-versions would work now, but a plain decorator won't get control if the
def fails, so the def name won't get bound:

 >>> def deco(f): print f; return f # noop
 ...
 >>> @deco
 ... def foo(x=1/2): pass
 ...
 <function foo at 0x02EF409C>
 >>> foo
 <function foo at 0x02EF409C>
 >>> del foo
 >>> foo
 Traceback (most recent call last):
   File "<stdin>", line 1, in ?
 NameError: name 'foo' is not defined
 >>> @deco
 ... def foo(x=1/0): pass
 ...
 Traceback (most recent call last):
   File "<stdin>", line 1, in ?
 ZeroDivisionError: integer division or modulo by zero
 >>> foo
 Traceback (most recent call last):
   File "<stdin>", line 1, in ?
 NameError: name 'foo' is not defined

>
>> [Note 1:]
>> Interestingly raise doesn't seem to have to be in the same frame or down-stack, so a decorator
>> called as above could re-raise:
>> 
>>  >>> def deco(**kw):
>>  ...     print kw
>>  ...     raise
>>  ...
>>  >>> try: 1/0
>>  ... except Exception, e: deco(__exception__=e)
>>  ...
>>  {'__exception__': <exceptions.ZeroDivisionError instance at 0x02EF190C>}
>>  Traceback (most recent call last):
>>    File "<stdin>", line 2, in ?
>>    File "<stdin>", line 1, in ?
>>  ZeroDivisionError: integer division or modulo by zero
>
>Interestin.
Yes, it would seem to give one the option of catching an exception and
doing some arbitarily (so long as not new-exception-raising) recursive
or deep calls and bailing out from some leaf point with a raise.

>
>When it comes to decorators, and now the with statements, I can't help 
>but feel that there's some sort of underlying concept that would work 
>better.  It has to do with generalizing flow control in a dynamic way 
>relative to an associated block.
>
>One thought is to be able to use a place holder in an expression list to 
>tell a statement when to do the following block of code.
it depends on scope and control aspects of what you mean by "block".
But I doubt if we have much chance of introducing something is one more
bf in the storm of "with" ideas that have already been discussed.
>
>I'll use 'do' here... since it's currently unused and use @@@ as the 
>place holder.
>
>(These are *NOT* thought out that far yet, I know...  just trying to 
>show a direction that might be possible.)
>
They strike me as a kind of macro idea where the only substitution argument
is the block suite that follows, which IMO is a severe limitation on both
the macro idea and the use of blocks ;-)

[...]
>
>I like the place holders because I think they make the code much more 
>explicit and they are more flexible because you can put them where you 
>need them.
Yes, but if you want to go that way, I'd want to have named place holders
and be able to refer to arbitrary things that make sense in the context.
>
>
>> orthogonal-musing-ly ;-)
>
>"Orthogonal is an unusual computer language in which your program flow 
>can go sideways. In actuality in can go in just about any direction you 
>could want."
>
>    http://www.muppetlabs.com/~breadbox/orth/

Interesting. And an implementation from our own Jeff Epler?
Regards,
Bengt Richter



More information about the Python-list mailing list