Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I do assignments in a list comprehension?

I want to use the assignment operator in a list comprehension. How can I do that?

The following code is invalid syntax. I mean to set lst[0] to an empty string '' if it matches pattern:

[ lst[0] = '' for pattern in start_pattern if lst[0] == pattern ]

Thanks!

like image 351
xunzhang Avatar asked Apr 24 '12 05:04

xunzhang


People also ask

Can you do assignment in list comprehension?

Using Assignment Expressions in List Comprehensions. We can also use assignment expressions in list comprehensions. List comprehensions allow you to build lists succinctly by iterating over a sequence and potentially adding elements to the list that satisfy some condition.

What is a proper way to write a list comprehension?

List comprehension is an elegant way to define and create lists based on existing lists. List comprehension is generally more compact and faster than normal functions and loops for creating list. However, we should avoid writing very long list comprehensions in one line to ensure that code is user-friendly.

How do list comprehensions work?

List comprehensions provide us with a simple way to create a list based on some sequence or another list that we can loop over. In python terminology, anything that we can loop over is called iterable. At its most basic level, list comprehension is a syntactic construct for creating lists from existing lists.

What is list comprehension give an example?

List comprehension offers a shorter syntax when you want to create a new list based on the values of an existing list. Example: Based on a list of fruits, you want a new list, containing only the fruits with the letter "a" in the name.


5 Answers

Python 3.8 will introduce Assignment Expressions.

It is a new symbol: := that allows assignment in (among other things) comprehensions. This new operator is also known as the walrus operator.

It will introduce a lot of potential savings w.r.t. computation/memory, as can be seen from the following snippet of the above linked PEP (formatting adapted for SO):

Syntax and semantics

In most contexts where arbitrary Python expressions can be used, a named expression can appear. This is of the form NAME := expr where expr is any valid Python expression other than an unparenthesized tuple, and NAME is an identifier.

The value of such a named expression is the same as the incorporated expression, with the additional side-effect that the target is assigned that value:

  1. Handle a matched regex

    if (match := pattern.search(data)) is not None:
        # Do something with match
    
  2. A loop that can't be trivially rewritten using 2-arg iter()

    while chunk := file.read(8192):
        process(chunk)
    
  3. Reuse a value that's expensive to compute

    [y := f(x), y**2, y**3]
    
  4. Share a subexpression between a comprehension filter clause and its output

    filtered_data = [y for x in data if (y := f(x)) is not None]
    

This is already available in the recently releases alpha version (not recommended for production systems!). You can find the release schedule for Python 3.8 here.

like image 163
n1k31t4 Avatar answered Oct 06 '22 05:10

n1k31t4


It looks like you are confusing list comprehension with looping constructs in Python.

A list comprehension produces -- a list! It does not lend itself to a single assignment in an existing list. (Although you can torture the syntax to do that...)

While it isn't exactly clear what you are trying to do from your code, I think it is more similar to looping over the list (flow control) vs producing a list (list comprehension)

Loop over the list like this:

for pattern in patterns:
   if lst[0] == pattern: lst[0]=''

That is a reasonable way to do this, and is what you would do in C, Pascal, etc. But you can also just test the list for the one value and change it:

if lst[0] in patterns: lst[0] = ''

Or, if you don't know the index:

i=lst.index[pattern]
lst[i]=''

or, if you have a list of lists and want to change each first element of each sublist:

for i, sublst in enumerate(lst):
    if sublst[i][0] in patterns: sublist[i][0]=''

etc, etc, etc.

If you want to apply something to each element of a list, then you can look at using a list comprehension, or map, or one of the many other tools in the Python kit.

Personally, I usually tend to use list comprehensions more for list creation:

 l=[[ x for x in range(5) ] for y in range(4)]  #init a list of lists...

Which is more natural than:

l=[]
for i in range(4):
   l.append([])
   for j in range(5):
      l[i].append(j)      

But to modify that same list of lists, which is more understandable?

This:

l=[['new value' if j==0 else l[i][j] for j in range(len(l[i]))] for i in range(len(l))]

or this:

for i,outter in enumerate(l):
    l[i][0]='new value'               

YMMV

Here is a great tutorial on this.

like image 25
dawg Avatar answered Oct 06 '22 05:10

dawg


The Python language has distinct concepts for expressions and statements.

Assignment is a statement even if the syntax sometimes tricks you into thinking it's an expression (e.g. a=b=99 works but is a special syntax case and doesn't mean that the b=99 is an expression like it is for example in C).

List comprehensions are instead expressions because they return a value, in a sense the loop they perform is an incident and the main point is the returned list.

A statement can contain expressions but an expression cannot contain statements.

That said however list item assigment to is internally converted to a method call (to allow creation of list-like objects) and method calls are expressions. Therefore you can technically use list item assignment in an expression:

[ lst.__setitem__(0, '') for pattern in start_pattern if lst[0] == pattern ]

This is however considered bad because it harms readability and how easy is to read source code is the main focus in the Python language. You should write instead for example...

for pattern in start_pattern:
    if lst[0] == pattern:
        lst[0] = ''

that by the way thanks to the in operator is equivalent to the even more readable

if lst[0] in start_pattern:
    lst[0] = ''

List comprehensions are used for their return value and they make a loop internally... If what you want is the loop then just write a loop... whoever will read the code trying to understand what it does would appreciate that a lot (and whoever includes yourself in a few weeks).

like image 38
6502 Avatar answered Oct 06 '22 05:10

6502


In short: you don't. List comprehensions are for generating lists, not modifying existing lists. If you want to modify a list, use a for loop, as that's what they're for.

The Pythonic way to write that code would be something like:

for pattern in start_pattern:
    if lst[0] == pattern:
        lst[0] = ''
        #the following assumes that pattern will never be ''
        #if that's possible, you can ignore this bit
        break

However, if you really, really want to do assignment inside one, and don't mind every Python programmer who ever deals with your code hating it for all eternity, there are a few functions you can use:

  • If the variable you want to assign to is a global, then you can do

        globals().update(var=value)
    
  • If the variable you want to assign to is a mutable sequence or a map (such as a list or a dict)

        list.__setitem__(index, value)
    
like image 33
Colin Valliant Avatar answered Oct 06 '22 05:10

Colin Valliant


As mentioned in the above answers there is no direct assignment possible in the list comprehension feature, but there is a hackable way to do it using the exec method to achieve the assignment.

Not just assignments we can pas any python expression in exec method to evaluate it.

In your case, it could be achieved by

  [ exec(f"lst[0] = ''") for pattern in start_pattern if lst[0] == pattern ]

If we have a list of objects and we want to assign a value to a specific attribute of the object on a specific condition then it could be like:

[ exec("p.age_status=child") for p in persons_obj_list if p.age <= 18 ]

Note: This will change the state of existing objects of a list and will not return the list of objects as it returns in normal use of list comprehention.

like image 45
Zubair Hassan Avatar answered Oct 06 '22 04:10

Zubair Hassan