[Python-ideas] Accessing the result of comprehension's expression from the conditional

Lie Ryan lie.1296 at gmail.com
Sun Jun 21 08:25:01 CEST 2009

Steven D'Aprano wrote:
> On Sun, 21 Jun 2009 04:08:05 am Lie Ryan wrote:
>> Steven D'Aprano wrote:
>>>> The advantage of F being callable is that it does not need
>>>> semantic change, the filtering part will be done before expression
>>>> just like it is right now.
>>> That's not true. It can't be true. If you want to filter on x**x
>>> being greater than 100, you need to calculate x**x first. In
>>> theory, a sufficiently clever compiler could recognise that, say,
>>> x**x < 100 implies 0 <= x < 3.59728 (approx), but in general, you
>>> can't predict the value of f(x) without actually calculating f(x).
>> Did you read the middle part of the post and the diagram, which
>> address the question you're asking and how it would be handled?
> Yes. It made no sense to me at all.
> Let's make a practical example:
> c = 1246158315.0  # approximately a week from now
> L = filter(lambda t: t > c, [time.time() for x in range(20)])
> becomes:
> L = [time.time() as F for x in range(20) if F() > c]
> How do you expect your proposed syntax to determine whether or not the 
> current time is greater than c without actually checking the current 
> time?

Of course it will call F(). F() will evaluate time.time() and cache it
so future call to F() do not need to call time.time() twice (resulting
in different time used for filter and expression).

> Note also that your proposal requires list comps to become like lambda. 
> Using your earlier example:
> [x**x as F for x in lst if F() < 100]
> This doesn't bind the value of x**x to the name F, but the expression 
> x**x to F. That makes it like a lambda:
> lambda x: x**x
> except the name x is implied and some sort of magic takes place to 
> ensure that by the time you call F(), the appropriate value of x is 
> still around. If this is to apply to generator expressions as well, 
> calling F() could occur some arbitrarily large time later.

F() could be called when the filtering takes place or not be called at
all, when it does gets called, the expression would be evaluated. In
effect this moves forward the expression evaluation to the middle of
filtering process.

> That means that 
> [time.time() for x in lst]
> will create a list that looks something like:
> [1245555766.5681751, 1245555767.2609128, ...]
> but your proposed:
> [time.time() as F for x in lst]
> will create a list something like:
> [<function F at 0x88a879c>, <function F at 0x88a8d84>, ...]

Where did you get that idea? The comprehension would call F before
appending it to the list. The equivalent for-loop syntax also clearly
stated that:

result = []
for x in lst:
    # F() ensures f(x) will be only ever be called once
    def F():
        nonlocal _cache
        if not _cache:
            _cache = f(x)
        return _cache

    _cache = None
    if g(F()):
        # here F is called before it's appended,
        # result is the same as calling f(x)
        # but prevents evaluating f(x) twice

More information about the Python-ideas mailing list