0

I found this example on the internet

def format_attributes(**attributes):
    """Return a string of comma-separated key-value pairs."""
    return ", ".join(
        f"{param}: {value}"
        for param, value in attributes.items()
    )

The syntax of the param passed to the join function caught my attention because it's sort of unusual. But it works

Doing some local testing with a minimum codebase I discovered that in:

def foo(res):
    return res

print(foo(f"{s}" for s in ["bar"]))

foo's syntax is valid and res ends up being a generator. However, if I try f"{s}" for s in ["bar"] standalone (no function in between), the expression just throws a SyntaxError: invalid syntax.

How come the f-string + for loop is valid and gets converted into a generator? What's happening under the hood when invoking foo function?

These other questions uses the same syntax:

But I found no comment explaining why this happens

0

3 Answers 3

1

The looping construct you're using is a generator expression. To write one as a stand-alone expression, you need to add parentheses around it:

genexp = (f"{s}" for s in ["bar"])

If the generator expression is the only argument to a function, you don't need double parentheses (but you do if there are other separate arguments). Contrast:

s = sum(i % 2 for i in some_sequence) # count of odd elements, no extra parentheses needed

vs:

print(*(i for i in some_sequence if i % 2), sep=",") # print odds, parens are needed this time

There's nothing special about f-string used in the generator expression in your code, any expression works the same way.

Sign up to request clarification or add additional context in comments.

1 Comment

I marked this one as the accepted answer because it explicitly states that no parenthesis is needed when a generator is used as a single argument. Thanks!
1

These are examples of generator expressions and don't necessarily have anything specific to f-strings or which functions you use with them.

e.g.

>>> x = [1, 2, 3, 4]
>>> sum(i%2==0 for i in x)
2

The example counts the number of even integers in the list.

You can read more about them here: https://dbader.org/blog/python-generator-expressions

1 Comment

I found the article useful. It describes syntax for generators in detail
0

The f-string has nothing to do with it.

Although a generator expression generally requires parentheses:

some_gen = (f"{s}" for s in ["bar"])
print(foo(some_gen))

the parentheses can be omitted when the generator expression is the only argument to a function call:

# These two calls are equivalent.
foo((f"{s}" for s in ["bar"]))
foo( f"{s}" for s in ["bar"] )

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.