This commit is contained in:
Donald Stufft 2015-08-29 10:40:35 -04:00
commit eb8155bac5
2 changed files with 85 additions and 8 deletions

View File

@ -208,6 +208,25 @@ Expressions cannot contain ':' or '!' outside of strings or parens,
brackets, or braces. The exception is that the '!=' operator is
special cased.
Escape sequences
----------------
Scanning an f-string for expressions happens after escape sequences
are decoded. Because hex(ord('{')) == 0x7b, the f-string
f'\\u007b4*10}' is decoded to f'{4*10}', which evaluates as the integer
40::
>>> f'\u007b4*10}'
'40'
>>> f'\x7b4*10}'
'40'
>>> f'\x7b4*10\N{RIGHT CURLY BRACKET}'
'40'
These examples aren't generally useful, they're just to show that
escape sequences are processed before f-strings are parsed for
expressions.
Code equivalence
----------------
@ -360,6 +379,25 @@ Leading and trailing whitespace in expressions is skipped
For ease of readability, leading and trailing whitespace in
expressions is ignored.
Evaluation order of expressions
-------------------------------
The expressions in an f-string are evaluated in left-to-right
order. This is detectable only if the expressions have side effects::
>>> def fn(l, incr):
... result = l[0]
... l[0] += incr
... return result
...
>>> lst = [0]
>>> f'{fn(lst,2)} {fn(lst,3)}'
'0 2'
>>> f'{fn(lst,2)} {fn(lst,3)}'
'5 7'
>>> lst
[10]
Discussion
==========
@ -403,7 +441,7 @@ used. A quick search of Python's standard library shows only a handful
of uses of string.Template, but hundreds of uses of str.format().
Another proposed alternative was to have the substituted text between
\{ and } or between \{ and \}. While this syntax would probably be
\\{ and } or between \\{ and \\}. While this syntax would probably be
desirable if all string literals were to support interpolation, this
PEP only supports strings that are already marked with the leading
'f'. As such, the PEP is using unadorned braces to denoted substituted
@ -473,6 +511,15 @@ use variables as index values::
See [#]_ for a further discussion. It was this observation that led to
full Python expressions being supported in f-strings.
Furthermore, the limited expressions that str.format() understands
need not be valid Python expressions. For example::
>>> '{i[";]}'.format(i={'";':4})
'4'
For this reason, the str.format() "expression parser" is not suitable
for use when implementing f-strings.
Triple-quoted f-strings
-----------------------
@ -593,15 +640,45 @@ having 2 expressions::
f'{x:.{width}}'
Expressions with side effects
-----------------------------
The same expression used multiple times
---------------------------------------
xxx
Every expression in braces in an f-string is evaluated exactly
once. If the same expression is used more than once in the same
f-string, it will be evaluated multiple times. However, it's undefined
which result will show up in the resulting string value. For purposes
of this section, two expressions are the same if they have the exact
same literal text defining them. For example, '{i}' and '{i}' are the
same expression, but '{i}' and '{ i}' are not, due to the extra space
in the second expression.
Expressions used multiple times
-------------------------------
For example, given::
xxx
>>> def fn(lst):
... lst[0] += 1
... return lst[0]
...
>>> lst=[0]
>>> f'{fn(lst)} {fn(lst)}'
'1 2'
The resulting f-string might have the value '1 2', '2 2', '1 1', or
even '2 1'.
However::
>>> lst=[0]
>>> f'{fn(lst)} { fn(lst)}'
'1 2'
This f-string will always have the value '1 2'. This is due to the two
expressions not being the same: the space in the second example makes
the two expressions distinct.
This restriction is in place in order to allow for a possible future
extension allowing translated strings, wherein the expression
substitutions would be identified by their text values as they show up
between the braces.
References
==========

View File

@ -166,7 +166,7 @@ following semantics::
__slots__ = ("raw_template", "parsed_fields", "field_values")
def __new__(cls, raw_template, parsed_fields, field_values):
self = super().__new__()
self = super().__new__(cls)
self.raw_template = raw_template
self.parsed_fields = parsed_fields
self.field_values = field_values