Re: decorators and closures

2011-11-22 Thread 88888 Dihedral
On Monday, November 21, 2011 10:44:34 PM UTC+8, Andrea Crotti wrote:
> With one colleague I discovered that the decorator code is always 
> executed, every time I call
> a nested function:
> 
> def dec(fn):
>  print("In decorator")
>  def _dec():
>  fn()
> 
>  return _dec
> 
> def nested():
>  @dec
>  def fun():
>  print("here")
> 
> nested()
> nested()
> 
> Will give:
> In decorator
> In decorator
> 
> So we were wondering, would the interpreter be able to optimize this 
> somehow?
> I was betting it's not possible, but I'm I would like to be wrong :)

I love to use decorators. I did the same things to  functions and structures in 
c long time ago. I think that  might be  the dark night era in  programming  in 
the early 90's long long ago. 

Cheers.   
-- 
http://mail.python.org/mailman/listinfo/python-list


Re: decorators and closures

2011-11-21 Thread Steven D'Aprano
On Mon, 21 Nov 2011 14:44:34 +, Andrea Crotti wrote:

> With one colleague I discovered that the decorator code is always
> executed, every time I call a nested function:

"def" is a statement which is executed at runtime. Often people will talk 
about "definition time" instead of "compile time".

Python compiles your source into byte code (compile time), then executes 
the byte code. The function doesn't actually get created until the byte 
code is executed, i.e. at run time. This is not as slow as it sounds, 
because the function is created from pre-compiled parts.

In effect, if you have a module:

x = 23
def spam(a):
print x
print x+1
return x**3


then the body of the function is compiled into a "code object" at compile 
time, and at runtime the function object itself is assembled from the 
code object, name, and whatever other bits and pieces are needed. In 
pseudo-code, the byte code looks like this:

bind name "x" to object 23
build a function object "spam" from code object
bind name "spam" to function object

The hard part is creating the code object, as that requires parsing the 
source code of the body and generating byte code. That's done once, ahead 
of time, so the actual "build a function" part is fast.

Now, if you have an ordinary nested function:

def spam(a):
def ham(b):
return a+b
return ham(a+42)  # returns a numeric value

or a closure:

def spam(a):
def ham(b):
return a+b
return ham  # returns a closure (function object)

the process is no different: the inner function doesn't get created until 
runtime, that is, when spam gets *called*. But it gets created from parts 
that were prepared earlier at compile time, and so is fast.

Add a decorator, and the basic process remains. Remember that decorator 
syntax is just syntactic sugar. This:

@decorator
def spam():
pass

is exactly the same as this:

def spam():
pass

spam = decorator(spam)

which clearly has to be done at runtime, not compile time. That applies 
regardless of whether the function is nested or top-level.



-- 
Steven
-- 
http://mail.python.org/mailman/listinfo/python-list


Re: decorators and closures

2011-11-21 Thread Andrea Crotti

On 11/21/2011 05:11 PM, Dave Angel wrote:


You didn't mention what version of Python you're running.  With Python 
2, I got very different results.  So I switched to Python 3.2, and I 
still don't get exactly what you have.


A closure is needed if there's some non-global data outside the 
function definition (code object) that's needed by the function 
object.  As you supply the code I don't need a closure.  But if I add 
a local variable in test_decorate(), and refer to it in dec(), then I 
get one.  Not the same as yours.


You left out the import and the definition line for test_decorate.  
Did you leave anything else?  And what version of Python are you 
using?  Are you perhaps running in a shell, as opposed to running code 
directly from a source file?


I use python 2.7, and actually the whole source is this (test_decorate.py):

def dec(fn):
  def _dec():
  fn()

  return _dec

@dec
def fun():
  print("here")

fun()


Using ipython:
import test_decorate
dis.dis(test_decorate)
--
http://mail.python.org/mailman/listinfo/python-list


Re: decorators and closures

2011-11-21 Thread Dave Angel

On 11/21/2011 10:35 AM, Andrea Crotti wrote:

On 11/21/2011 03:06 PM, Dave Angel wrote:

Your function 'nested' isn't nested, 'fun' is. What you discovered is
that a decorator is always executed, every time a nested decorated
function is defined.

You've also ust proved that it would be an incompatible change.
Doesn't that answer the question? An optimizer that changes the
behavior isn't usually desirable.


Yes sure I think it makes perfectly sense, because you actually redefine
a local variable every time..

Another thing (which was also the reason of the subject), I tried to
disassemble the following:
def dec(fn):
def _dec():
fn()

return _dec

@dec
def fun():
print("here")

fun()

And I get this:
In [29]: dis.dis(test_decorate)
Disassembly of dec:
2 0 LOAD_CLOSURE 0 (fn)
3 BUILD_TUPLE 1
6 LOAD_CONST 1 ()
9 MAKE_CLOSURE 0
12 STORE_FAST 1 (_dec)

5 15 LOAD_FAST 1 (_dec)
18 RETURN_VALUE

Disassembly of fun:
3 0 LOAD_DEREF 0 (fn)
3 CALL_FUNCTION 0
6 POP_TOP
7 LOAD_CONST 0 (None)
10 RETURN_VALUE


Looking up the definition of the single calls didn't help much, so why
do we need
for example MAKE_CLOSURE?
Is MAKE_CLOSURE just more generic maybe?



You didn't mention what version of Python you're running.  With Python 
2, I got very different results.  So I switched to Python 3.2, and I 
still don't get exactly what you have.


A closure is needed if there's some non-global data outside the function 
definition (code object) that's needed by the function object.  As you 
supply the code I don't need a closure.  But if I add a local variable 
in test_decorate(), and refer to it in dec(), then I get one.  Not the 
same as yours.


You left out the import and the definition line for test_decorate.  Did 
you leave anything else?  And what version of Python are you using?  Are 
you perhaps running in a shell, as opposed to running code directly from 
a source file?




--

DaveA
--
http://mail.python.org/mailman/listinfo/python-list


Re: decorators and closures

2011-11-21 Thread Andrea Crotti

On 11/21/2011 03:06 PM, Dave Angel wrote:
Your function 'nested' isn't nested, 'fun' is.  What you discovered is 
that a decorator is always executed, every time a nested decorated 
function is defined.


You've also ust proved that it would be an incompatible change.  
Doesn't that answer the question?  An optimizer that changes the 
behavior isn't usually desirable.


Yes sure I think it makes perfectly sense, because you actually redefine 
a local variable every time..


Another thing (which was also the reason of the subject), I tried to 
disassemble the following:

  def dec(fn):
  def _dec():
  fn()

  return _dec

  @dec
  def fun():
  print("here")

  fun()

And I get this:
  In [29]: dis.dis(test_decorate)
  Disassembly of dec:
2   0 LOAD_CLOSURE 0 (fn)
3 BUILD_TUPLE  1
6 LOAD_CONST   1 (0x1c4b930, file "test_decorate.py", line 2>)

9 MAKE_CLOSURE 0
   12 STORE_FAST   1 (_dec)

5  15 LOAD_FAST1 (_dec)
   18 RETURN_VALUE

  Disassembly of fun:
3   0 LOAD_DEREF   0 (fn)
3 CALL_FUNCTION0
6 POP_TOP
7 LOAD_CONST   0 (None)
   10 RETURN_VALUE


Looking up the definition of the single calls didn't help much, so why 
do we need

for example MAKE_CLOSURE?
Is MAKE_CLOSURE just more generic maybe?
--
http://mail.python.org/mailman/listinfo/python-list


Re: decorators and closures

2011-11-21 Thread Dave Angel

On 11/21/2011 09:44 AM, Andrea Crotti wrote:
With one colleague I discovered that the decorator code is always 
executed, every time I call

a nested function:

def dec(fn):
print("In decorator")
def _dec():
fn()

return _dec

def nested():
@dec
def fun():
print("here")

nested()
nested()

Will give:
In decorator
In decorator

So we were wondering, would the interpreter be able to optimize this 
somehow?

I was betting it's not possible, but I'm I would like to be wrong :)
Your function 'nested' isn't nested, 'fun' is.  What you discovered is 
that a decorator is always executed, every time a nested decorated 
function is defined.


You've also ust proved that it would be an incompatible change.  Doesn't 
that answer the question?  An optimizer that changes the behavior isn't 
usually desirable.



--

DaveA

--
http://mail.python.org/mailman/listinfo/python-list