开发者

Naming variables within nested list comprehensions in Python?

Like the title says, is there any way to name variables (i.e., lists) used within a nested list comprehension in Python?

I could come up with a fitting example, but I think the question is clear enough.

Here is an example of pseudo code:

[... [r for r in some_list if r.some_attribute == something_from_within_this_list comprehension] ... [r for r in some_list if r.some_attribute == something_from_within_this_list comprehension] ...]

Is there any way to avoid the repetition here and simply add a variable for this temporary list only for use within the list comprehension?

CLARIFICATION: The list comprehension is already working fine, so it's not a question of 'can it be done with a list comprehension'. 开发者_StackOverflowAnd it is quicker than it's original form of a for statement too, so it's not one of those 'for statements vs list comprehensions' questions either. It is simply a question of making the list comprehension more readable by making variable names for variables internal to the list comprehension alone. Just googling around I haven't really found any answer. I found this and this, but that's not really what I am after.


Based on my understanding of what you want to do, No you cannot do it.

You cannot carry out assignments in list comprehensions because a list comprehension is essentially of the form

[expression(x, y) for x in expression_that_creates_a_container 
                  for y in some_other_expression_that_creates_a_container(x)
                  if predicate(y, x)]

Granted there are a few other cases but they're all about like that. Note that nowhere does there exist room for a statement which is what a name assignment is. So you cannot assign to a name in the context of a list comprehension except by using the for my_variable in syntax.

If you have the list comprehension working, you could post it and see if it can be simplified. Solutions based on itertools are often a good alternative to burly list comprehensions.


I think I understand exactly what you meant, and I came up with a "partial solution" to this problem. The solution works fine, but is not efficent.

Let me explain with an example:

I was just trying to solve a Pythagorean triplet which sum was 1000. The python code to solve it is just:

def pythagoreanTriplet(sum):
    for a in xrange(1, sum/2):
        for b in xrange(1, sum/3):
            c = sum - a - b
            if c > 0 and c**2 == a**2 + b**2:
               return a, b, c

But I wanted to code it in a functional programming-like style:

def pythagoreanTriplet2(sum):
    return next((a, b, sum-a-b) for a in xrange(1, sum/2) for b in xrange(1, sum/3) if (sum-a-b) > 0 and (sum-a-b)**2 == a**2 + b**2)

As can be seen in the code, I calc 3 times (sum-a-b), and I wanted to store the result in an internal varible to avoid redundant calculation. The only way I found to do that was by adding another loop with a single value to declare an internal variable:

def pythagoreanTriplet3(sum):
    return next((a, b, c) for a in xrange(1, sum/2) for b in xrange(1, sum/3) for c in [sum-a-b] if c > 0 and c**2 == a**2 + b**2)

It works fine... but as I said at the begin of the post, is not an efficent method. Comparing the 3 methods with cProfile, the time required for each method is the next one:

  • First method: 0.077 seconds
  • Secnd method: 0.087 seconds
  • Third method: 0.109 seconds


Some people could classify the following as a "hack", but it is definitely useful in some cases.

f = lambda i,j: int(i==j) #A dummy function (here Kronecker's delta)    
a = tuple(tuple(i + (2+f_ij)*j + (i + (1+f_ij)*j)**2
                for j in range(4)
                for f_ij in (f(i,j),) ) #"Assign" value f(i,j) to f_ij.
          for i in range(4) )
print(a)
#Output: ((0, 3, 8, 15), (2, 13, 14, 23), (6, 13, 44, 33), (12, 21, 32, 93))

This approach is particularly convenient if the function f is costly to evaluate. Because it is somewhat unusual, it may be a good idea to document the "assignment" line, as I did above.


I'm just gonna go out on a limb here, because I have no idea what you really are trying to do. I'm just going to guess that you are trying to shoehorn more than you should be into a single expression. Don't do that, just assign subexpressions to variables:

sublist = [r for r in some_list if r.some_attribute == something_from_within_this_list comprehension]
composedlist = [... sublist ... sublist ...]


This feature was added in Python 3.8 (see PEP 572), it's called "assignment expressions" and the operator is := .

Examples from the documentation:

results = [(x, y, x/y) for x in input_data if (y := f(x)) > 0]

stuff = [[y := f(x), x/y] for x in range(5)]

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜