12

I would like to create an f-string that could be used multiple times as the following code with format:

TEXT_AMOUNT = 'text {amount}'


def f1(beginning):
    return beginning + TEXT_AMOUNT.format(amount=10)


def f2(ending):
    return TEXT_AMOUNT.format(amount=100) + ending

How can I achieve the same functionality with using an f-string? I tried:

TEXT_AMOUNT = f'text {amount}'


def f1(beginning):
    amount = 100
    return beginning + TEXT_AMOUNT


def f2(ending):
    amount = 10
    return TEXT_AMOUNT + ending

However, I get the following error:

NameError: name 'amount' is not defined
lmiguelvargasf
  • 63,191
  • 45
  • 217
  • 228
  • 1
    An f-string is executable code. Doing `TEXT_AMOUNT = f'text {amount}'` expects the global variable `amount` to be defined so that it can be substituted into the value assigned to `TEXT_AMOUNT` *when that assignment is executed.* You can't store an f-string in a variable. In other words, for your use case, use `str.format()`. – kindall Sep 09 '18 at 21:30
  • It's worth skimming through [PEP 498](https://www.python.org/dev/peps/pep-0498/) to see the reasoning behind why f-strings were designed this way. – abarnert Sep 09 '18 at 21:36
  • Does this answer your question? [How to postpone/defer the evaluation of f-strings?](https://stackoverflow.com/questions/42497625/how-to-postpone-defer-the-evaluation-of-f-strings) – James Dec 16 '19 at 23:03

3 Answers3

20

You can store it as lambda function:

TEXT_AMOUNT = lambda amount: f'text {amount}'
print(TEXT_AMOUNT(10))

out: 'text 10'
Vespertilio
  • 301
  • 2
  • 5
14

You can't.

An f-string isn't a kind of string, it's a kind of string literal, which is evaluated immediately. You can't store an f-string in a variable to be evaluated later, or accept one from a user, etc.1 This is the only reason that they're safe.

So, what if you do want to use a format multiple times (or one taken from a user, etc.)? You use str.format.

Occasionally, you need to capture all of the locals and globals the same way an f-string does, but to do it explicitly. Because this is a rare case (and potentially a security hole), it's intentionally a bit ugly:

TEXT_AMOUNT = 'text {amount}'

def f1(beginning):
    amount = 100
    return beginning + TEXT_AMOUNT.format(**locals(), **globals())

This makes you think about what you're writing—you don't really want globals here, right? So leave it off. And it also signals the reader—if you're pulling in locals, they'll want to see that the string really is a constant in your source that isn't doing anything dangerous.


1. Well, you could use an f-string inside a string that you pass to eval… but that's a terrible idea.

abarnert
  • 354,177
  • 51
  • 601
  • 671
10

But you can put f-string in a function and reuse it that way

def TEXT_AMOUNT(amount):
    return f'text {amount}'

def f1(beginning):
    return beginning + " " + TEXT_AMOUNT(amount=10)

def f2(ending):
    return TEXT_AMOUNT(amount=100) + " " + ending

print(f1("first"))
print(f2("last"))

first text 10

text 100 last

bd2357
  • 704
  • 9
  • 17