f-strings don't behave nicely when used with dictionaries, as mentioned here.
Here is an example of the not-so-nice behavior:
d = {'foo': 'bar'}
# Both work as expected
d["foo"]
d['foo']
# This only works when different quotations are used in the inner and outer strings
f'{d["foo"]}'
f"{d['foo']}"
# This doesn't work
f'{d['foo']}'
f"{d["foo"]}"
# The .format() method doesn't care
'{}'.format(d['foo'])
The last two f-strings listed result in a SyntaxError: invalid syntax, which happens because the string '{d['foo']}' is evaluated as '{d['foo']}'.
What is the underlying reason everything inside the curly brackets of f-strings doesn't get evaluated separately, as when using the old .format() method, and what could possibly be the reason for implementing f-strings in this way?
I love f-strings, but this seems like a point in favor of the old method.
F-strings are literal strings. Including unescaped quotes within quotes (of the same type) is invalid syntax. This makes sense, since the result is ambiguous: the interpreter will not know when the string should end. One traditional way of including quotes within quotes is to use a backslash. But PEP498 forbids backslashes in expressions within f-strings:
Backslashes may not appear inside the expression portions of
f-strings...You can use a different type of quote inside the expression...
Therefore, the only way left to access a dictionary value given a key in an f-string expression is to use a different type quote. Using single quotes, or double quotes, everywhere is ambiguous and gives SyntaxError.
str.format is a regular method, and as such works differently: d['foo'] is evaluated before the string is constructed. Just like when you feed arguments to a function, the arguments are evaluated before the function does anything.
This has nothing to do with f-strings. f strings are common strings once evaluated. What you are trying would be a problem with standard strings too
The problem is that
'a "b" c'
is declares the literal a "b" c
while
'a 'b' c'
the quotes close and reopen. So, it is equivalent to string a, followed by variable b, followed by string c.
That's the whole reason python supports both types of quotation marks
According to PEP 498, f-strings rely on the implementation of regular string literals, and as such are subject to the same constraints. In addition to those, f-strings have their own constraints, such as the exclusion of backslashes anywhere within expressions.
A rationale for the choice of implementation has not been published, but ease of implementation, backwards compatibility, syntax highlighting, and consistency with existing interpolation syntax all featured in discussions among core developers on the publicly accessible mailing lists.1
A central point of contention was whether the proposed concept constitutes a string with special properties or a string interjected with code.2 The former view was favored. PEP 536, embodying the dissenting view and additionally seeking to lift several syntactic constraints, was subsequently filed.
Based on this discussion, a tentative compromise of prohibiting backslashes within expressions was agreed upon, leaving string delimiter inversion as the only remaining option for indexing dictionaries within f-string expressions.
Select discussions predating f-string introduction (Python 3.6):
[Python-ideas] Briefer string format
Re: [Python-ideas] Briefer string format
Re: [Python-ideas] Briefer string format
[Python-ideas] String interpolation for all literal strings
[Python-Dev] PEP-498: Literal String Formatting
[Python-Dev] PEP 498 f-string: is it a preprocessor?
[Python-Dev] PEP 498 href="interpolated f-string" tweak
[Python-Dev] Parsing f-strings from PEP 498 -- Literal String Interpolation
[Python-ideas] Let’s make escaping in f-literals impossible
Related
Is it possible to do the opposite of eval() in python which treats inputs as literal strings? For example f(Node(2)) -> 'Node(2)' or if there's some kind of higher-order operator that stops interpreting the input like lisp's quote() operator.
The answer to does Python have quote is no, Python does not have quote. There's no homoiconicity, and no native infrastructure to represent the languages own source code.
Now for what is the opposite of eval: you seem to be missing some part the picture here. Python's eval is not the opposite of quote. There's three types values can be represented as: as unparsed strings, as expressions, and as values (of any type).
quote and LISP-style "eval" convert between expressions and values.
Parsing and pretty printing form another pair between expressions and strings.
Python's actual eval goes directly from strings to values, and repr goes back (in the sense of printing a parsable value1).
Since SO does not support drawing diagrams:
So if you are looking for the opposite of eval, it's either trivially repr, but that might not really help you, or it would be the composition of quote and pretty priniting, if there were quoting in Python.
This composition with string functions a bit cumbersome, which is why LISPs let the reader deal with parsing once and for all, and from that point on only go between expressions and values. I don't know any language that has such a "quote + print" function, actually (C#'s nameof comes close, but is restricted to names).
Some caveats about the diagram:
It does not commute: you have deal with pure values or disregard side effects, for one, and most importantly quote is not really a function in some sense, of course. It's a special form. That's why repr(x) is not the same as prettyprint(quote(x)), in general.
It's not depicting pairs of isomorphisms. Multiple expressions can eval to the same value etc.
1That's not always the case in reality, but that's what it's suppsedly there for.
This question already has answers here:
f-strings vs str.format()
(5 answers)
Closed 2 years ago.
These two print statements produce the same results and at least to me the first version looks more readable.
Should I stick with the f' version because it will cause issues for me later on or cause worse performance or does not follow the present Python standards? Or is it only a question of consistent usage of one of these versions?
print('My first bicycle was a ' + bicycles[1])
print(f'My first bicycle was a {bicycles[1]})')
From PEP 498, a PEP (Python Enhancement Proposals) dedicated to Literal String Interpolation,
F-strings provide a way to embed expressions inside string literals,
using a minimal syntax. It should be noted that an f-string is really
an expression evaluated at run time, not a constant value. In Python
source code, an f-string is a literal string, prefixed with ‘f’, which
contains expressions inside braces. The expressions are replaced with
their values.
The main point here is the highlighted text. That means it is way better in evaluation due to the internal implementation of ASTs (abstract syntax trees) by the CPython compiler which makes them fast.
Also, I think it is more readable and in case of more variables in your string it provides better readability. Multi-line string interpolation can be performed easily using this syntax.
For example,
f'''
My first bicycle was a {bicycles[0]}
It has color {bicycles[0]["color"]}
and has {bicycles[0]["gears"]} gears.
'''
I am working my way through PEP 498. Maybe I do not quite get the concept, but how could I possibly convert a regular Python string into a formatted string literal and evaluate it at run-time?
Imagine you have a string:
some_string = 'Name: {name}'
If it was a formatted string literal, the following would work:
name = 'Demo'
some_string_literal = f'Name: {name}'
some_string_literal == 'Name: Demo' # returns True
Ignoring the question of whether this makes sense at all, how could I e.g. read the contents of some_string from a file at run-time, turn it into some_string_literal and evaluate it? My understanding of the underlying CPython implementation is that string literals are interpreted at compile-time (translation into byte code).
I am aware of the more "explicit" option of just using ...
some_string = 'Name: {name}'
some_string.format(name = 'Demo') == 'Name: Demo' # returns True
... but this is not what I am looking for.
EDIT: It was noted in the comments that the "explicit" option is what I should be looking for. I'd agree that what I am asking here is definitely insecure in a number of ways. Nevertheless, I am interested in whether there is a way to do this or not.
PEP 498 says
Because the compiler must be involved in evaluating the expressions contained in the interpolated strings, there must be some way to denote to the compiler which strings should be evaluated.
In other words, the f has an effect on the way the string literal is parsed, just as r does. So you have to invoke the parser for the interpolation to work.
You want to read data into some_string and turn it into a string literal that can be parsed as an f-string. That is not very different from wanting to read data into some_lambda_expression and turning it into a lambda expression. You can do it, of course, using eval, because eval is a way to turn the contents of a string variable into code, by invoking the parser. I know that is not what you wanted. But you can't parse some_string as an f-string without invoking the parser, so you can't have what you want.
my_dic={"Vajk":"vékony","Bibi":'magas'}
my_dic['Bibi']
'magas'
We can see, that we need the ' to refer to the value for key 'Bibi'. But if want to use the same format in .format(), it gives this error:
print("úgy érzem, hogy Bibi nagyon {0['Bibi']}".format(my_dic))
Traceback (most recent call last):
File "<ipython-input-109-52760dad9136>", line 1, in <module>
print("úgy érzem, hogy Bibi nagyon {0['Bibi']}".format(my_dic))
KeyError: "'Bibi'"
I have to use the reference without ', then it works.
print("úgy érzem, hogy Bibi nagyon {0[Bibi]}".format(my_dic))
úgy érzem, hogy Bibi nagyon magas
Why doesn't the first work, and why does the second? It should be the opposite, first should work, and second shouldn't.
First, some terminology:
'Bibi' is a string literal, syntax to create a string value. Your keys are strings, and using a string literal you can specify one of those keys.
You could use a variable instead; assign as string value to a variable and use the variable to get an item from your dictionary:
foo = 'Bibi' # or read this from a file, or from a network connection
print(my_dic[foo]) # -> magas
In string formatting, the {...} are replacement fields. The str.format() method provides values for the replacement fields.
The 0[Bidi] part in the {...} replacement field syntax is the field name. When you use [...] in a field name, it is a compound field name (there are multiple parts). The [...] syntax is usually referred to as indexing.
The format used in field names is deliberately kept simple, and is only Python-like. The syntax is simplified to limit what it can be used for, to constrain the functionality to something that is usually safe to use.
As such, if you use a compound name with getitem [...] indexing syntax, names are treated as strings, but you don't use quotes to create the string. You could not pass in a variable name anyway, there is no need to contrast between 'name' (a string) and name (a variable).
In other words, in a Python expression, my_dic[foo] works by looking up the value of the variable foo, and that is a different concept from using a string literal like my_dic['Bidi']. But you can't use variables in a field name, in a str.format() operation, using {0[foo]} should never find the variable foo.
The original proposal to add the feature explains this as:
Unlike some other programming languages, you cannot embed arbitrary expressions in format strings. This is by design - the types of expressions that you can use is deliberately limited. Only two operators are supported: the '.' (getattr) operator, and the '[]' (getitem) operator. The reason for allowing these operators is that they don't normally have side effects in non-pathological code.
and
It should be noted that the use of 'getitem' within a format string is much more limited than its conventional usage. In the above example, the string 'name' really is the literal string 'name', not a variable named 'name'. The rules for parsing an item key are very simple. If it starts with a digit, then it is treated as a number, otherwise it is used as a string.
Keeping the syntax simple makes templates more secure. From the Security Considerations section:
Barring that, the next best approach is to ensure that string formatting has no side effects. Because of the open nature of Python, it is impossible to guarantee that any non-trivial operation has this property. What this PEP does is limit the types of expressions in format strings to those in which visible side effects are both rare and strongly discouraged by the culture of Python developers.
Permitting {0[foo]} to look up variables in the current scope could easily produce side effects, while treating foo as a string instead means you can know, with certainty, that it'll always be the string 'foo' and not something else.
If you are only using string literals and not dynamic values (so 'some {} template'.format(...) and not some_variable.format(...), and you are using Python 3.6 or newer, you can use formatted string literals instead, as you can then use full Python expressions:
print(f"úgy érzem, hogy Bibi nagyon {my_dic['Bibi']}")
In an f string, you use actual Python expressions instead of field names, so you use quotes again to pass in a string literal. Because they are string literals, they are evaluated right where they are defined, and you as a developer can see what local variables are available, so presumably you know how to keep that secure.
It should be the opposite, first should work, and second shouldn't.
No, it shouldn't because 'Bibi' inside a double quoted string is a quoted string not just Bibi. You can check this simply as following:
In [51]: "'Bibi'" == "Bibi"
Out[51]: False
If in first case the key was "'Bibi'" Then it'd worked perfectly:
In [49]: my_dic={"Vajk":"vékony","'Bibi'":'magas'}
In [50]: print("úgy érzem, hogy Bibi nagyon {0['Bibi']}".format(my_dic))
úgy érzem, hogy Bibi nagyon magas
The reason that why it doesn't accept "Bibi" in first case and doesn't give you a the expected result is that Python looks for everything between brackets in dictionary and in this case you have "'Bibi'" inside the brackets not 'Bibi'.
I'm trying out Python 3.6. Going through new code, I stumbled upon this new syntax:
f"My formatting string!"
It seems we can do things like this:
>>> name = "George"
>>> print(f"My cool string is called {name}.")
My cool string is called George.
Can anyone shed some light on the inner workings of this? In particular what is the scope of the variables that an f-prefixed string can take?
See PEP 498 Literal String Interpolation:
The expressions that are extracted from the string are evaluated in the context where the f-string appeared. This means the expression has full access to local and global variables. Any valid Python expression can be used, including function and method calls.
So the expressions are evaluated as if they appear in the same scope; locals, closures, and globals all work the same as in other code in the same context.
You'll find more details in the reference documentation:
Expressions in formatted string literals are treated like regular Python expressions surrounded by parentheses, with a few exceptions. An empty expression is not allowed, and a lambda expression must be surrounded by explicit parentheses. Replacement expressions can contain line breaks (e.g. in triple-quoted strings), but they cannot contain comments. Each expression is evaluated in the context where the formatted string literal appears, in order from left to right.
Since you are trying out a 3.6 alpha build, please do read the What's New In Python 3.6 documentation. It summarises all changes, including links to the relevant documentation and PEPs.
And just to be clear: 3.6 isn't released yet; the first alpha is not expected to be released until May 2016. See the 3.6 release schedule.
f-strings also support any Python expressions inside the curly braces.
print(f"My cool string is called {name.upper()}.")
It might also be worth noting that this PEP498 has a backport to Python <3.6
pip install fstring
from fstring import fstring
x = 1
y = 2.0
plus_result = "3.0"
print fstring("{x}+{y}={plus_result}")
# Prints: 1+2.0=3.0
letter f for "format" as in f"hello {somevar}. This little f before the "(double-quote) and the {} characters tell python 3, "hey, this string needs to be formatted. So put these variable in there and format it.".
hope this is clear.