decorators as generalized pre-binding hooks

Ron Adam rrr at ronadam.com
Sun Jul 10 01:35:01 EDT 2005


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.

How about if you make it optional too?

if keep_log:
    @log_of_interesting_values
b = get_value(c,d):

Or...

@@keeplog log_of_interesting_values     # if keeplog decorate.
b = get_value(c,d)

Just a thought.

> 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.


> 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?

> [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.

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.

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.)


do f=open(filename); @@@; f.close():   # do the block where @@@ is.
    for line in f:
       print line,
    print


And an alternate version similar to a "with" statement.

try f=open(filename); @@@; finally f.close():
    for line if f:
       print line,
    print

Maybe the exception could be held until after the try line is complete?


The place holder idea might be useful for decorating as well.  But I'm 
not sure how the function name and arguemtns would get passed to it.

do deco(@@@):
    def foo():
        pass

or maybe it would need to be...

do f=@@@, deco(f):
    def foo()
        pass

As I said, it still needs some thinking.. ;-)


Maybe leaving off the colon would use the following line without 
indenting as per your example?

do deco(@@@)
b = 42

It doesn't quite work this way I think. Possibly having a couple of 
different types of place holder symbols which alter the behavior might work?

do deco($$$)   # $$$ intercept name binding operation?
b = 42


Well, it may need to be a bit (or a lot) of changing.  But the idea of 
controlling flow with a place holder symbol might be a way to generalize 
some of the concepts that have been floating around into one tool.

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.


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


;-)

Cheers,
Ron


> Regards,
> Bengt Richter



More information about the Python-list mailing list