0

I have a big/small problem. I load the whole content from a file into a string. Then I want to add strings.

My example file:

Titel
Name: {name_string}
Firstname: {firstname_string}
...

One way would be .format(name_string=name_string, firstname_string=firstname_string) But that takes very much time to do when I have a big textfile and it destroys (The .format() line would be 50 lines) the whole code.

I tried to use .format() without anything in it. But that did not work.

Is there a way to do it with f-strings? Or any cleaner way?

Brian61354270
  • 8,690
  • 4
  • 21
  • 43
Manuservus
  • 19
  • 1
  • 10
  • 1
    No, you can only use f-strings in actual Python code. Maybe you should look for a _templating engine_. – jonrsharpe May 15 '21 at 15:47
  • 3
    If you want to insert all variables automatically, you can use `.format(**locals())`. If not, please explain the problem further. – Alex Hall May 15 '21 at 15:49
  • 1
    if all the variables you want to use are already in the global or local scope, you can unpack the dictionary of `globals()` or `locals()` into the arguments of format as such: `template.format(**globals())` – Aaron May 15 '21 at 15:50
  • @Aaron Wich way is faster? – Manuservus May 15 '21 at 15:51
  • 1
    It seems like this is less a problem with formatting strings and more a problem with having 50 individual but related variables. You might consider refactoring to use a dictionary for this data. – Mark May 15 '21 at 15:52
  • 1
    @Manuservus then one of the names you referenced in the template was missing in the local scope, and you need to define it. That's why I'd recommend not just using the local vars, and use a dictionary you build yourself then unpack into the format function: `template.format(**mydict)` – Aaron May 15 '21 at 15:52
  • 1
    It smells to me like the sort of problems you get in general with using global variables... It's easy to loose track of them. Instead, it's better to be explicit even if it means a few more lines of text in your code. It's worth a few extra lines of code if it makes the code more explicit (unless you're playing code golf). – Aaron May 15 '21 at 15:54
  • @AlexHall Solved, thanks – Manuservus May 15 '21 at 15:54
  • 1
    @Aaron Could you write your solutions as an answer? – Manuservus May 15 '21 at 15:58

1 Answers1

1

"True" f-strings can only be literal strings within a python file, so for the sake of semantics, what you have is actually a regular string which you're calling .format on. If you want the same functionality of "true" f-strings (pulling local variables for the {replacement_values}), you need to give the format method a dictionary of those values, which can be easily obtained with the builtin functions: locals(), and globals().

This brings up one of the age-old questions of programming: Are global variables bad? In short... they have their uses, but if you use them as a crutch, sometimes it will break. The problem you mentioned in the comments is exactly one such example. You presumably had variable definitions for this template sprinkled throughout your code, and one of them had a slightly different name, or you missed one that was supposed to be filled in. This is why I would actually recommend you don't use globals or locals in favor of creating your own dictionary of inputs. This is basically what you already had in your question, but there are a few ways to clean up the text to make it not look so bad in you .py file:

1. Honestly, just leave it as you had it, or just split the args to format over multiple lines. There's nothing wrong with a long function args section, and it's extremely clear what the intent is.

with open('my_template.txt') as f:
    filled_template = f.read().format(
        name_string=name_string, 
        firstname_string=firstname_string,
        # ...
    )

2. Create your own dictionary of input values which you can then pass and unpack into format. This is helpful if you have other important things to do next to the line where you're filling the template, and you don't want them to get visually lost.

f_args = {}

f_args["name_string"] = input("enter a name")
f_args["firstname_string"] = f_args["name_string"].split()[0] #extract the first name from a full name

with open('my_template.txt') as f:
    filled_template = f.read().format(**f_args) #unpack the args dict into the `format` call

#### or if you already have the values, and just want to collect them...

f_args = {
    "name_string":name_string, 
    "firstname_string":firstname_string,
    # ...
}
Aaron
  • 10,133
  • 1
  • 24
  • 40
  • I don't want to give the impression necessarily that globals are totally evil, and you should never use them, but this just doesn't strike me as a place they are necessary. If they were never useful, languages wouldn't have them. – Aaron May 15 '21 at 16:23