31

A simple question about Python syntax. I want to assign a value from a function to a variable during the condition for a while loop. When the value returned from the function is false, the loop should break. I know how to do it in PHP.

while (($data = fgetcsv($fh, 1000, ",")) !== FALSE) 

However when I try a similar syntax in Python I get a syntax error.

3
  • 4
    If you had a function fgetcsv in Python that returned either a string or False, so exactly the same pattern as you have in PHP, then for data in iter(lambda: fgetcsv(fh, 1000, ","), False): would have the effect you desire.
    – Duncan
    Commented Nov 4, 2013 at 13:04
  • @Duncan - Huh... what if you wanted a more generic iter that would consider any Falsey value as the sentinel? Commented Feb 8, 2016 at 2:31
  • I updated my answer for 2020; the := operator exists since Python 3.8. Commented Feb 18, 2020 at 21:03

4 Answers 4

48

2020 answer:

Since Python 3.8, the "walrus operator" := exists that does exactly what you want:

while data := fgetcsv(fh, 1000, ",") != False:
    pass

(if that fgetcsv function existed)

2013 answer: You can't do that in Python, no assignment in expressions. At least that means you won't accidentally type == instead of = or the other way around and have it work.

Traditional Python style is to just use while True and break:

while True:
    data = fgetcsv(fh, 1000, ",")
    if not data:
        break
    # Use data here

But nowadays I'd put that in a generator:

def data_parts(fh):
    while True:
        data = fgetcsv(fh, 1000, ",")
        if not data:
            break
        yield data

so that in the code that uses the file, the ugliness is hidden away:

for data in data_parts(fh):
    # Use data here

Of course if it's actually CSV reading that you're doing, use the csv module.

2
  • Thanks. I am going to use the infinitive loop.
    – Borut Flis
    Commented Nov 4, 2013 at 12:50
  • 3
    @RemcoGerlich Great example of a simple generator instead of while loop. Commented Jan 21, 2016 at 16:25
38

You cannot use assignment in an expression. Assignment is itself a statement, and you cannot combine Python statements.

This is an explicit choice made by the language designers; it is all too easy to accidentally use one = and assign, where you meant to use two == and test for equality.

Move the assignment into the loop, or assign before the loop, and assign new values in the loop itself.

For your specific example, the Python csv module gives you a higher-level API and you'd be looping over the csv.reader() instead:

with open(csvfilename, 'rb') as csvfh:
    reader = csv.reader(csvfh)
    for row in reader:

I rarely, if ever, need to assign in a loop construct. Usually there is a (much) better way of solving the problem at hand.

That said, as of Python 3.8 the language will actually have assignment expressions, using := as the assignment operator. See PEP 572. Assignment expressions are actually useful in list comprehensions, for example, when you need to both include a method return value in the list you are building and need to be able to use that value in a test.

Now, you'd have to use a generator expression:

absolute = (os.path.abspath(p) for p in files)
filtered = [abs for abs in absolute if included(abs)]

but with assignment expressions you can inline the os.path.abspath() call:

filtered = [abs for p in files if included(abs := os.path.abspath(p))]
4
  • Thanks. I actually need this for something else than csv , sorry if I was confusing.
    – Borut Flis
    Commented Nov 4, 2013 at 12:49
  • @BorutFlis: Sure, but again, make sure you check for better ways of achieving this. Commented Nov 4, 2013 at 12:51
  • 1
    Just learning Python and love it for its conciseness. This is the first thing that upset me, even though I can understand the choice made by the language designers...
    – opncow
    Commented Apr 20, 2019 at 16:28
  • 4
    @opncow: actually, Python 3.8 will have an assignment expression. See PEP 572. Commented Apr 20, 2019 at 17:07
5

Python 3.8 pep-0572 now address this case using the new notation :=. Have a look :)

For example:

while chunk := file.read(8192):
   process(chunk)
4

I wrote a little Python module, which I call let, which allows you to perform a variable assignment anywhere that a function is allowed.

Install it like this:

pip install let

I believe the following will accomplish what you're looking for:

from let import let

while let(data = fgetcsv(fh, 1000, ',')):
    # Do whatever you'd like with data here

However... Duncan's comment the original question saying to use iter is interesting. I wasn't aware of the function until he brought it up, and I now believe it may be a better solution than mine. It's debatable - iter requires a sentinel to be explicitly provided, whereas mine doesn't care and simply waits for fgetcsv to return any Falsey value.

Not the answer you're looking for? Browse other questions tagged or ask your own question.