[Python-3000] Change to class construction?

Nick Coghlan ncoghlan at gmail.com
Sat Jul 7 16:15:54 CEST 2007


Georg Brandl wrote:
> Greg Ewing schrieb:
>> Phillip J. Eby wrote:
>>> This looks like a bug to me.  A list comprehension's local scope 
>>> should be the locals of the enclosing code, even if its loop indexes 
>>> aren't exposed to that scope.
>> It sounds like list comprehensions are being implemented
>> using genexps behind the scenes now.
> 
> That's not true, but the implementation is somewhat similar in that
> the code is executed in its own function context.

Georg is correct. A list comprehension like:

[(x * y) for x in seq1 for y in seq2]

expands to the following in 2.x (% prefixes the compiler's hidden 
variables):

   %n = []
   for x in seq1:
     for y in seq2:
       %n.append(x*y) # Special opcode, not a normal call

In py3k it expands to:

   def <anon>(outermost):
     %0 = []
     for x in outermost:
       for y in seq2:
         %0.append(x*y) # Special opcode, not a normal call
     return %0
   %n = <anon>(seq1)

Python's scoping rules are somewhat tricky - doing it this way means we 
know they are being applied the same way in list and set comprehensions 
as they are applied in generator expressions, even if it isn't quite as 
fast as the 2.x approach to comprehensions.

Another significant benefit from a maintainability point of view is that 
the 3 kinds of comprehension (list, set, genexp) now follow the same 
code path through the compiler, with only minor variations in the 
setup/cleanup code and the statement inside the innermost loop.

>> Is this wise? In a recent thread, I suggested that one
>> of the reasons for keeping the LC syntax was that it
>> could be faster than list(genexp). Has anyone investigated
>> whether any speed is being lost by making them equivalent?
> 
> I don't remember the details, but IIRC the new LC implementation
> was not slower than the 2.x one. Nick should know more about that.

Inside a function, Py3k is slower by a constant amount relative to 2.x 
(the cost of creating and calling a function object) regardless of the 
length of the resulting list/set. At module level, Py3k will typically 
be faster, as the fixed cost from the anonymous function object will be 
overtaken by the speedup from the iteration variables becoming function 
locals instead of module globals.

The Py3k comprehensions are still significantly faster than the 
equivalent generator expressions, as they still avoid suspending and 
resuming a generator for each value in the resulting sequence.

The bit that makes all of this tricky isn't really hiding the iteration 
variables from the containing scope - it's making sure that the body of 
the comprehension can still see them after you have done so 
(particularly challenging if the comprehension itself contains a lambda 
expression, or another comprehension/genexp).

Cheers,
Nick.

-- 
Nick Coghlan   |   ncoghlan at gmail.com   |   Brisbane, Australia
---------------------------------------------------------------
             http://www.boredomandlaziness.org


More information about the Python-3000 mailing list