Observations on the three pillars of Python execution

2011-08-05 Thread Eric Snow
In Python, three types of objects have special syntax and mechanics
for their instantiation, during which a code object is generated:
modules, classes, and functions.  Each has its own role to play and
the differences between them mostly reflect that.  Here are some
observations (based on default behavior):


Object creation relative to code object creation:
(M) before
(C) after
(F) after

Object creation relative to code object execution:
(M) before
(C) after
(F) before

Object available during code object execution:
(M) no
(C) no
(F) no

Code object destiny:
(M) executed once at definition time, then discarded
(C) executed once at definition time, then discarded
(F) not executed at definition time, stored on the function
 object for execution with each __call__() invocation.

Execution locals is available during code object execution:
(M) as globals()
(C) as locals()
(F) as locals()

Object namespace is execution locals of code object:
(M) yes
(C) more or less
(F) completely distinct

Unique syntax:
(M) 'import' statement
(C) 'class' statement
(F) 'def' statement

Mechanism triggered by respective syntax:
(M) import machinery (import.c/importlib)
(C) internal, but partially exposed via metaclasses and the
 __build_class__ builtin
(F) internal, nothing exposed

Override capability:
(M) complete capability through builtin __import__ and PEP
  302 import hooks
(C) partial capability, before code object execution through
 metaclass __prepare__() and after through __build_class__()
 and metaclass __call__(), __new__(), and __init__()
(F) no capability

Post instantiation modification capability:
(M) yes
(C) yes
(F) mostly (some attributes are read-only)

Mechanism to instantiate outside of respective unique syntax:
(M) imp.new_module(), types.ModuleType(), type(module)()
(C) type()
(F) types.FunctionType(), type(f)()

Type extensibility:
(M) Not relative to 'import' syntax (by default)
(C) Complete
(F) No

Name available during definition execution:
(M) as __name__
(C) only through metaclass __prepare__()
(F) through inspect.currentframe().f_code.co_name

Name available on object as __name__:
(M) yes
(C) yes
(F) yes


Corrections, additions, and comment are welcome.

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


Re: Observations on the three pillars of Python execution

2011-08-05 Thread Thomas Jollans
On 05/08/11 09:20, Eric Snow wrote:
 Object available during code object execution:
 (M) no
 (C) no
 (F) no
(F) yes.

cf. recursion.

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


Recursive functions (was Re: Observations on the three pillars of Python execution)

2011-08-05 Thread Chris Angelico
On Fri, Aug 5, 2011 at 9:22 AM, Thomas Jollans t...@jollybox.de wrote:
 On 05/08/11 09:20, Eric Snow wrote:
 Object available during code object execution:
 (M) no
 (C) no
 (F) no
 (F) yes.

 cf. recursion.

Is it? As I understand it, a Python function is not able to reference
itself but must reference its own name. This means that assigning
that function to something else stops it being recursive:

#
 def foo(x):
print(x)
if x3: foo(x-1)

 foo(5)
5
4
3
 bar=foo
 bar(5)
5
4
3
 def foo(x):
print(Foo,x)
if x3: foo(x-1)

 foo(5)
Foo 5
Foo 4
Foo 3
 bar(5)
5
Foo 4
Foo 3
#

bar() is not a recursive function, even though foo() was and is. I've
just played around with a decorator, though; this appears to work:

 def recursive(f):
l=list(f.__defaults__)
l[-1]=f
f.__defaults__=tuple(l)
return f
 @recursive
def quux(x,quux=None):
print(Quux,x)
if x3: quux(x-1)
 foo=quux
 def quux(x):
print(Stopping.)
 quux(5)
Stopping.
 foo(5)
Quux 5
Quux 4
Quux 3

Effectively, the decorator binds the function's own self to its last
default parameter, thus holding a cyclic reference (unlikely to be an
issue - people won't be creating and destroying functions frequently,
so GC issues shouldn't arise). The last parameter doesn't have to have
the name of the function; this works with lambdas:

 recursive(lambda x,_=None: print(Lambda,x) or (x3 and _(x-1) or None))(5)
Lambda 5
Lambda 4
Lambda 3

Yes, this is a pretty stupid example. But is this sort of decorator
useful? It's not like people regularly want recursive lambdas.

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


Re: Observations on the three pillars of Python execution

2011-08-05 Thread Steven D'Aprano
Eric Snow wrote:

 In Python, three types of objects have special syntax and mechanics
 for their instantiation, during which a code object is generated:
 modules, classes, and functions.  

I believe you are labouring under a misapprehension. Modules and classes
don't generate code objects.

The simplest possible module just has a name:

 import sys
 module = type(sys)
 module(name)
module 'name' (built-in)

Or if you prefer to do it the normal way by importing from source code,
the simplest source code of all is just an empty file.

A module is an object with a __dict__, a __name__, and a __docstring__ which
defaults to None. That's it. No code objects, unless you populate the
__dict__ with functions, but it is the *functions* that have the code
objects, not the module itself. The module is just a container.

Classes are similar. The simplest class:

class K:
pass

or if you prefer:

 type('K', (), {})
class '__main__.K'


Again, no code objects unless you populate the dict with functions (which
get turned into methods). Again, the class object is just a container. A
more complex container than modules, with a richer set of inherited
behaviour, but still just a container.

The signature for creating a function is very different:

help(type(lambda: None)) = 

class function(object)
 |  function(code, globals[, name[, argdefs[, closure]]])
 |
 |  Create a function object from a code object and a dictionary.
 |  The optional name string overrides the name from the code object.
 |  The optional argdefs tuple specifies the default argument values.
 |  The optional closure tuple supplies the bindings for free variables.


The simplest function I can think of:

 co = compile('pass', '', 'single')
 f = type(lambda: None)(co, {})

Even simpler than

def f():
pass

Can you see why?

If you inspect a function object, you will see that it always has a code
object, even if the code object does nothing but return None:

 import dis
 dis.dis(f.func_code)
  1   0 LOAD_CONST   0 (None)
  3 RETURN_VALUE


There may be some other obscure built-in type that includes code objects,
but I can't imagine what it would be. I feel confident in saying that
functions, and functions alone, contain code. Even methods are just
wrappers around functions. Even built-in functions like len don't contain
code! (Or at least, their code isn't accessible from Python.) Which makes
sense, if you think about it: their code is part of the Python virtual
machine, not the object.


-- 
Steven

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


Re: Recursive functions (was Re: Observations on the three pillars of Python execution)

2011-08-05 Thread Steven D'Aprano
Chris Angelico wrote:

 On Fri, Aug 5, 2011 at 9:22 AM, Thomas Jollans t...@jollybox.de wrote:
 On 05/08/11 09:20, Eric Snow wrote:
 Object available during code object execution:
 (M) no
 (C) no
 (F) no
 (F) yes.

 cf. recursion.
 
 Is it? As I understand it, a Python function is not able to reference
 itself but must reference its own name.

That is correct. Recursion in Python is implemented simply by name lookup.


 This means that assigning 
 that function to something else stops it being recursive:

An easier way to demonstrate the process:

 def f(x):
... print x
... if x  0: f(x-1)
...
 f(3)
3
2
1
0
 g = f
 del f
 g(3)
3
Traceback (most recent call last):
  File stdin, line 1, in module
  File stdin, line 3, in f
NameError: global name 'f' is not defined


-- 
Steven

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


Re: Observations on the three pillars of Python execution

2011-08-05 Thread Eric Snow
On Fri, Aug 5, 2011 at 8:36 AM, Steven D'Aprano
steve+comp.lang.pyt...@pearwood.info wrote:
 Eric Snow wrote:

 In Python, three types of objects have special syntax and mechanics
 for their instantiation, during which a code object is generated:
 modules, classes, and functions.

 I believe you are labouring under a misapprehension. Modules and classes
 don't generate code objects.

Sorry for any confusion.  I was talking specifically about the their
instantiation through their respective special syntax.  During that
process a code object is generated for each.  For classes and modules
the code object is executed and then discarded.  I covered this
explicitly in one of the observations.


 The simplest possible module just has a name:

 import sys
 module = type(sys)
 module(name)
 module 'name' (built-in)

 Or if you prefer to do it the normal way by importing from source code,
 the simplest source code of all is just an empty file.

 A module is an object with a __dict__, a __name__, and a __docstring__ which
 defaults to None. That's it. No code objects, unless you populate the
 __dict__ with functions, but it is the *functions* that have the code
 objects, not the module itself. The module is just a container.

 Classes are similar. The simplest class:

 class K:
    pass

 or if you prefer:

 type('K', (), {})
 class '__main__.K'


 Again, no code objects unless you populate the dict with functions (which
 get turned into methods). Again, the class object is just a container. A
 more complex container than modules, with a richer set of inherited
 behaviour, but still just a container.

 The signature for creating a function is very different:

 help(type(lambda: None)) =

 class function(object)
  |  function(code, globals[, name[, argdefs[, closure]]])
  |
  |  Create a function object from a code object and a dictionary.
  |  The optional name string overrides the name from the code object.
  |  The optional argdefs tuple specifies the default argument values.
  |  The optional closure tuple supplies the bindings for free variables.


 The simplest function I can think of:

 co = compile('pass', '', 'single')
 f = type(lambda: None)(co, {})

 Even simpler than

 def f():
    pass

 Can you see why?

 If you inspect a function object, you will see that it always has a code
 object, even if the code object does nothing but return None:

 import dis
 dis.dis(f.func_code)
  1           0 LOAD_CONST               0 (None)
              3 RETURN_VALUE


 There may be some other obscure built-in type that includes code objects,
 but I can't imagine what it would be. I feel confident in saying that
 functions, and functions alone, contain code. Even methods are just
 wrappers around functions. Even built-in functions like len don't contain
 code! (Or at least, their code isn't accessible from Python.) Which makes
 sense, if you think about it: their code is part of the Python virtual
 machine, not the object.

Agreed that [non-builtin] functions are the only objects that have a
code object attribute.  However, they are not the only objects for
which a code object is generated and executed.

I've always found your frequent responses very insightful and
thoughtful, Steven, and I appreciate that.  In this case I can only
imagine that my opening statement was unclear enough that you did not
continue to read the rest, where the context of my point was more
clear (I hope :).  Regardless, thanks for taking a look.

-eric



 --
 Steven

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

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


Re: Observations on the three pillars of Python execution

2011-08-05 Thread Steven D'Aprano
Eric Snow wrote:

 On Fri, Aug 5, 2011 at 8:36 AM, Steven D'Aprano
 steve+comp.lang.pyt...@pearwood.info wrote:
 Eric Snow wrote:

 In Python, three types of objects have special syntax and mechanics
 for their instantiation, during which a code object is generated:
 modules, classes, and functions.

 I believe you are labouring under a misapprehension. Modules and classes
 don't generate code objects.
 
 Sorry for any confusion.  I was talking specifically about the their
 instantiation through their respective special syntax.  During that
 process a code object is generated for each.


Do you believe that this process of generating a code object and throwing it
away is a part of the Python language specification, which any compiler
must do in order to call itself Python, or a mere implementation detail?

Is this documented somewhere? If it is not documented, what makes you think
that it happens at all? You are writing as if it were self-evidently true,
but I don't believe it is self-evident at all. I think you need to
demonstrate the truth of two of those three pillars, not just take them for
granted.


 For classes and modules 
 the code object is executed and then discarded.  I covered this
 explicitly in one of the observations.

I think your definition of explicitly and mine differ here.


 Agreed that [non-builtin] functions are the only objects that have a
 code object attribute.  However, they are not the only objects for
 which a code object is generated and executed.

Are you talking about the fact that importing a module, class statements and
function statements all involve executing a block of code?

How does that differ from executing any other fragment of code?

Modules are special, of course, because they can get compiled to byte-code,
but I trust you're not talking about .pyc files. 



-- 
Steven

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


Re: Observations on the three pillars of Python execution

2011-08-05 Thread Mel
Steven D'Aprano wrote:

 There may be some other obscure built-in type that includes code objects,
 but I can't imagine what it would be. I feel confident in saying that
 functions, and functions alone, contain code. Even methods are just
 wrappers around functions. Even built-in functions like len don't contain
 code! (Or at least, their code isn't accessible from Python.) Which makes
 sense, if you think about it: their code is part of the Python virtual
 machine, not the object.

Interesting question.  Iterators seem to have code objects:

Python 2.6.5 (r265:79063, Apr 16 2010, 13:09:56) 
[GCC 4.4.3] on linux2
Type help, copyright, credits or license for more information.
 a = [1, 2, 3]
 b = (x for x in a)
 dir(b)
['__class__', '__delattr__', '__doc__', '__format__', '__getattribute__', 
'__hash__', '__init__', '__iter__', '__name__', '__new__', '__reduce__', 
'__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', 
'__subclasshook__', 'close', 'gi_code', 'gi_frame', 'gi_running', 'next', 
'send', 'throw']
 for name in dir(b):
...   print name, type (getattr (b, name))
... 
__class__ type 'type'
__delattr__ type 'method-wrapper'
__doc__ type 'NoneType'
__format__ type 'builtin_function_or_method'
__getattribute__ type 'method-wrapper'
__hash__ type 'method-wrapper'
__init__ type 'method-wrapper'
__iter__ type 'method-wrapper'
__name__ type 'str'
__new__ type 'builtin_function_or_method'
__reduce__ type 'builtin_function_or_method'
__reduce_ex__ type 'builtin_function_or_method'
__repr__ type 'method-wrapper'
__setattr__ type 'method-wrapper'
__sizeof__ type 'builtin_function_or_method'
__str__ type 'method-wrapper'
__subclasshook__ type 'builtin_function_or_method'
close type 'builtin_function_or_method'
gi_code type 'code'
gi_frame type 'frame'
gi_running type 'int'
next type 'method-wrapper'
send type 'builtin_function_or_method'
throw type 'builtin_function_or_method'


in the form of the gi_code attribute.  No idea what it's for, although no 
reason to believe it shouldn't be there.  (Very interesting demo you gave of 
primitive object creation.  I' awed.)

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


Re: Observations on the three pillars of Python execution

2011-08-05 Thread Terry Reedy

On 8/5/2011 4:22 AM, Thomas Jollans wrote:

On 05/08/11 09:20, Eric Snow wrote:

Object available during code object execution:
(M) no
(C) no
(F) no

(F) yes.

cf. recursion.


Recursion only happens through runtime name resolution, not through 
direct access to the function or code object from within the code.


--
Terry Jan Reedy

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


Re: Observations on the three pillars of Python execution

2011-08-05 Thread Eric Snow
On Fri, Aug 5, 2011 at 11:29 AM, Steven D'Aprano
steve+comp.lang.pyt...@pearwood.info wrote:
 Eric Snow wrote:

 On Fri, Aug 5, 2011 at 8:36 AM, Steven D'Aprano
 steve+comp.lang.pyt...@pearwood.info wrote:
 Eric Snow wrote:

 In Python, three types of objects have special syntax and mechanics
 for their instantiation, during which a code object is generated:
 modules, classes, and functions.

 I believe you are labouring under a misapprehension. Modules and classes
 don't generate code objects.

 Sorry for any confusion.  I was talking specifically about the their
 instantiation through their respective special syntax.  During that
 process a code object is generated for each.


 Do you believe that this process of generating a code object and throwing it
 away is a part of the Python language specification, which any compiler
 must do in order to call itself Python, or a mere implementation detail?

That's a great point which I hadn't considered.  Honestly, I only used
my experience with CPython in making these observations.  After
reviewing the language reference I see that I missed out on a bunch of
nomenclature that would have made things more clear, and I got a few
points wrong, which you pointed out.  :)

Regarding code objects and classes, your are right.  The language
reference indicates the following:

The class’s suite is then executed in a new execution frame...When
the class’s suite finishes execution, its execution frame is discarded
but its local namespace is saved. [1]

So the use of code objects for execution is an implementation detail.
Instead of code object I should have referred to the code executed
in the execution frame or just to the frame.  Incidently, I had not
realized that the builtin __build_class__() is also an implementation
detail[3].

For modules, the language reference doesn't say anything about how
execution is accomplished, which only matters when execution is
involved in the creation of the module object.  It does refer to
importlib as a reference implementation[4].  The order-of-operations
observations I made are based on that reference implementation.


 Is this documented somewhere? If it is not documented, what makes you think
 that it happens at all? You are writing as if it were self-evidently true,
 but I don't believe it is self-evident at all. I think you need to
 demonstrate the truth of two of those three pillars, not just take them for
 granted.


 For classes and modules
 the code object is executed and then discarded.  I covered this
 explicitly in one of the observations.

 I think your definition of explicitly and mine differ here.


 Agreed that [non-builtin] functions are the only objects that have a
 code object attribute.  However, they are not the only objects for
 which a code object is generated and executed.

 Are you talking about the fact that importing a module, class statements and
 function statements all involve executing a block of code?

 How does that differ from executing any other fragment of code?

The difference is that modules, classes, and functions (really the
function body) are code blocks tied to syntax that results in module,
type, and function objects.  There are other code blocks but none of
them have a unique syntax, much less one that results in an object of
the corresponding type[5].  This is relevant for trying to find the
object that corresponds to an execution frame, which is what led me to
my original post and drove the direction of the observations I made.

Anyway, I appreciate the feedback!  I'm going to have to revisit my
observations with the language definition in hand.  You've been really
insightful, as usual.

-eric


[1] http://docs.python.org/dev/reference/compound_stmts.html#class-definitions
[2] http://docs.python.org/dev/reference/datamodel.html#metaclasses
[3] http://mail.python.org/pipermail/python-3000/2007-March/006338.html
[4] http://docs.python.org/dev/reference/simple_stmts.html#the-import-statement
[5] http://docs.python.org/dev/reference/executionmodel.html#naming



 Modules are special, of course, because they can get compiled to byte-code,
 but I trust you're not talking about .pyc files.



 --
 Steven

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

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


Re: Observations on the three pillars of Python execution

2011-08-05 Thread Steven D'Aprano
Mel wrote:

 Steven D'Aprano wrote:
 
 There may be some other obscure built-in type that includes code objects,
 but I can't imagine what it would be. I feel confident in saying that
 functions, and functions alone, contain code. Even methods are just
 wrappers around functions. Even built-in functions like len don't contain
 code! (Or at least, their code isn't accessible from Python.) Which makes
 sense, if you think about it: their code is part of the Python virtual
 machine, not the object.
 
 Interesting question.  Iterators seem to have code objects:
[...]

Generators. But nice catch, thank you!

Iterators are *any* object which obeys the iterator protocol, that is, have
a next() method and an __iter__() method which behave in the expected way.
Iterators are a duck-type. Generators, whether created from a generator
expression or a generator function, are an actual type.

 type(x for x in (1,2))
class 'generator'


-- 
Steven

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


Re: Observations on the three pillars of Python execution

2011-08-05 Thread Steven D'Aprano
Eric Snow wrote:

 On Fri, Aug 5, 2011 at 11:29 AM, Steven D'Aprano
 steve+comp.lang.pyt...@pearwood.info wrote:
[...]
 Do you believe that this process of generating a code object and throwing
 it away is a part of the Python language specification, which any
 compiler must do in order to call itself Python, or a mere
 implementation detail?
 
 That's a great point which I hadn't considered.  Honestly, I only used
 my experience with CPython in making these observations.  After
 reviewing the language reference I see that I missed out on a bunch of
 nomenclature that would have made things more clear, and I got a few
 points wrong, which you pointed out.  :)
 
 Regarding code objects and classes, your are right.  The language
 reference indicates the following:
 
 The class’s suite is then executed in a new execution frame...When
 the class’s suite finishes execution, its execution frame is discarded
 but its local namespace is saved. [1]


It turns out that in CPython 2.5 at least, I'm strictly wrong and you got it
right, at least for classes:

 code = compile(class K: pass, '', 'exec')
 dis.dis(code)
  1   0 LOAD_CONST   0 ('K')
  3 LOAD_CONST   3 (())
  6 LOAD_CONST   1 (code object K at 0xb7e8ad10,
file , line 1)
  9 MAKE_FUNCTION0
 12 CALL_FUNCTION0
 15 BUILD_CLASS
 16 STORE_NAME   0 (K)
 19 LOAD_CONST   2 (None)
 22 RETURN_VALUE


So a code object is compiled, turned into a function, executed, the results
turned into a class, and the code object and function thrown away.

Is this an implementation detail? I would say so. The semantics of Python
the language are different from the details of it's virtual machine. Surely
we would be allowed to call something Python if it executed the body of the
class statement *without* creating a code object first? The important part
is *execute the body of the class statement*, not building the code object.
The later is merely a means to an end.


 So the use of code objects for execution is an implementation detail.
 Instead of code object I should have referred to the code executed
 in the execution frame or just to the frame.

Unless you really intend to talk about implementation details, I think you
should keep the discussion as high-level as possible. I'd talk about
executing blocks of code, and not even mention execution frames unless you
need to understand the role of frames during execution.


-- 
Steven

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