Docorator Disected

Ron_Adam radam2 at tampabay.rr.com
Tue Apr 5 14:59:32 EDT 2005


On Tue, 05 Apr 2005 06:52:58 GMT, bokr at oz.net (Bengt Richter) wrote:

>>Ok, yes, besides the globals, but I figured that part is obvious so I
>>didn't feel I needed to mention it.  The function call works the same
>>even though they are not nested functions.
>
>I am afraid that is wrong. But be happy, this may be the key to what ISTM
>is missing in your concept of python functions ;-)

The expression in the form of "function(args)(args)" is the same
pattern in two "different" cases, which was all that I was trying to
say.  Not that the exact process of the two different cases were the
same. 

>So, no, it does not "work the same." In fact, it is a misconception to talk about
>a nested fee as if it existed ready to call in the same way as foo. It doesn't
>exist that way until the fee def is EXECUTED, producing the callable fee.

Ok, I'm going to have to be more careful in how I phrase things I
think, I tend to over-genralize a bit. I said they were "the same",
but meant similar, a mistake in wording, but not in my understanding. 

But this is a good point. In my example the calling expression does
not yet know who the next tuple of arguments will go to until foo
returns it. That part is the same, but as you point out in a nested
scope foo defines fee then returns it. And in the non nested example
fee is already defined before foo is called.  And they must use
globals to communicate because they are not share the same name space.
They differ because fee is temporary, in the nested version, only
existing until the expression foo(arg)(arg) is evaluated.  It never
gets assigned a name in foo's parent name space. Do I have that
correct?


>We can use dis to see the above clearly:

Love the byte code walk through, Thanks.  Is there a resource that
goes in depth on python byte code and the compiler?  I haven't been
able to find much on it on google.

> >>> import time
> >>> def globalfun(): return '[%s]'%time.ctime()
> ...
> >>> foo()
> <function fee at 0x02EF802C>
> >>> foo()(111, 222)
> (111, 222)
> >>> foo()(333)
> (333, '[Mon Apr 04 22:31:23 2005][Mon Apr 04 22:31:23 2005]')
> >>> foo()(333)
> (333, '[Mon Apr 04 22:31:37 2005][Mon Apr 04 22:31:37 2005]')

I like your idea of using time stamps to trace code! :)


>[...]
>>Today I believe I have the correct view as I've said this morning. I
>>could be wrong yet again. I hope not though I might have to give up
>>programming. :/
>Don't give up. It would be boring if it were all instantly clear.
>The view is better after an enjoyable hike, and some of the flowers
>along the way may turn out prettier than whatever the vista at the
>top may be ;-)

I won't give up, at most I would take a break, but I love programming
too much to give it up.  ;-)


>Maybe the above will help make functions and decorators a little easier
>to understand.

I understand functions, sometimes it's difficult to describe just what
it is I don't understand yet, and sometimes I fool myself by jumping
to an invalid conclusion a little too quickly.  But I do this for
enjoyment and learning, so I'm not constrained by the need to not make
mistakes, (those are just part of learning in my oppinion), as I would
if my job depended on it.  However it's a little frustrating when my
inability to write well, gets in the way of expressing myself
accurately.

But a few questions remain...

When a @decorator statement is found, How does the compiler handle it?

Let me see if I can figure this out...using dis. :)

>>> from dis import dis
>>> def deco1(d1): return d1

>>> def func1(f1):
	@deco1
	def func2(f2):
		return f2
	return func2(f1)

>>> func1(2)
2

>>> dis(deco1)
  1           0 LOAD_FAST                0 (d1)
              3 RETURN_VALUE        
>>> dis(func1)
  2           0 LOAD_GLOBAL              0 (deco1)
              3 LOAD_CONST               1 (<code object func2 at
00B45CA0, file "<pyshell#11>", line 2>)
              6 MAKE_FUNCTION            0
              9 CALL_FUNCTION            1
             12 STORE_FAST               1 (func2)

  5          15 LOAD_FAST                1 (func2)
             18 LOAD_FAST                0 (f1)
             21 CALL_FUNCTION            1
             24 RETURN_VALUE        

I'm not sure how to interpret this... Line 5 and below is the return
expression. The part above it is the part I'm not sure about.

Is the first CALL_FUNCTION calling deco1 with the result of the
defined functions reference, as it's argument? Then storing the result
of deco1 with the name func2?

If so the precompiler/parser is replacing the @deco1 with a call to
the deco1 function like this.

    deco1( (def func2(f2):return f2) )

But this causes an illegal syntax error on the def statement. So you
can't do it directly. Or is there yet another way to view this? :)

Cheers,
Ron

 



More information about the Python-list mailing list