From 8af0c666ad99ba27ede4352bc8714c333e9d8d84 Mon Sep 17 00:00:00 2001 From: "Eric V. Smith" Date: Thu, 27 Aug 2015 10:20:59 -0400 Subject: [PATCH 01/10] Added a note that the str.format() parser is not suitable for use when implementing f-strings. --- pep-0498.txt | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/pep-0498.txt b/pep-0498.txt index d58f7f01f..85b028f80 100644 --- a/pep-0498.txt +++ b/pep-0498.txt @@ -470,6 +470,15 @@ use variables as index values:: >>> f'a={d[a]}' 'a=20' +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. + See [#]_ for a further discussion. It was this observation that led to full Python expressions being supported in f-strings. From fac2b212affadfcaab5160412664e6599df07c95 Mon Sep 17 00:00:00 2001 From: "Eric V. Smith" Date: Thu, 27 Aug 2015 10:48:24 -0400 Subject: [PATCH 02/10] Fixed call to super().__new__. --- pep-0501.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pep-0501.txt b/pep-0501.txt index e975afe41..a640de636 100644 --- a/pep-0501.txt +++ b/pep-0501.txt @@ -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 From 174ea8308e3db3835f833b8dbcfebfdfc79bc111 Mon Sep 17 00:00:00 2001 From: "Eric V. Smith" Date: Fri, 28 Aug 2015 11:03:07 -0400 Subject: [PATCH 03/10] Escape some backslashes. --- pep-0498.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pep-0498.txt b/pep-0498.txt index 85b028f80..d5ce55ce9 100644 --- a/pep-0498.txt +++ b/pep-0498.txt @@ -403,7 +403,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 From 75f463a37554177ed13401a08b86f8c3dbbe9091 Mon Sep 17 00:00:00 2001 From: "Eric V. Smith" Date: Fri, 28 Aug 2015 11:16:23 -0400 Subject: [PATCH 04/10] Added section on order of evaluation. --- pep-0498.txt | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/pep-0498.txt b/pep-0498.txt index d5ce55ce9..86190ca24 100644 --- a/pep-0498.txt +++ b/pep-0498.txt @@ -602,10 +602,24 @@ having 2 expressions:: f'{x:.{width}}' -Expressions with side effects ------------------------------ +Evaluation order of expressions +------------------------------- -xxx +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] Expressions used multiple times ------------------------------- From 3e2bcc696628d2a1dc9e3de16f8c8923eba4cfbc Mon Sep 17 00:00:00 2001 From: "Eric V. Smith" Date: Fri, 28 Aug 2015 11:41:58 -0400 Subject: [PATCH 05/10] Moved a sentence to where it makes more sense. --- pep-0498.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pep-0498.txt b/pep-0498.txt index 86190ca24..963ec0ccd 100644 --- a/pep-0498.txt +++ b/pep-0498.txt @@ -470,6 +470,9 @@ use variables as index values:: >>> f'a={d[a]}' 'a=20' +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:: @@ -479,9 +482,6 @@ need not be valid Python expressions. For example:: For this reason, the str.format() "expression parser" is not suitable for use when implementing f-strings. -See [#]_ for a further discussion. It was this observation that led to -full Python expressions being supported in f-strings. - Triple-quoted f-strings ----------------------- From 088fee8cb8c35ac4a5b08383b2aa8abf792605df Mon Sep 17 00:00:00 2001 From: "Eric V. Smith" Date: Fri, 28 Aug 2015 12:21:04 -0400 Subject: [PATCH 06/10] Moved order of evaluation into the Specification section, since it's guaranteed. --- pep-0498.txt | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/pep-0498.txt b/pep-0498.txt index 963ec0ccd..00cabd0fc 100644 --- a/pep-0498.txt +++ b/pep-0498.txt @@ -360,6 +360,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 ========== @@ -602,25 +621,6 @@ having 2 expressions:: f'{x:.{width}}' -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] - Expressions used multiple times ------------------------------- From c0c7101fa5e1d081b7770c249197fe41643a94bc Mon Sep 17 00:00:00 2001 From: "Eric V. Smith" Date: Fri, 28 Aug 2015 14:26:58 -0400 Subject: [PATCH 07/10] Added specification for order of escape sequence processing. --- pep-0498.txt | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/pep-0498.txt b/pep-0498.txt index 00cabd0fc..3adac9d6b 100644 --- a/pep-0498.txt +++ b/pep-0498.txt @@ -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 decode. Because hex(ord('{')) == 0x7b, that means that 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 ---------------- From 35c0d9cc757f62eba97669799b87393c772ed1cd Mon Sep 17 00:00:00 2001 From: "Eric V. Smith" Date: Fri, 28 Aug 2015 14:27:58 -0400 Subject: [PATCH 08/10] Fixed awkward wording. --- pep-0498.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pep-0498.txt b/pep-0498.txt index 3adac9d6b..79d482946 100644 --- a/pep-0498.txt +++ b/pep-0498.txt @@ -212,9 +212,9 @@ Escape sequences ---------------- Scanning an f-string for expressions happens after escape sequences -are decode. Because hex(ord('{')) == 0x7b, that means that the -f-string f'\u007b4*10}' is decoded to f'{4*10}', which evaluates as -the integer 40:: +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' From 5d0b58c678e0facd76fc8217ab447598c2f5824b Mon Sep 17 00:00:00 2001 From: "Eric V. Smith" Date: Fri, 28 Aug 2015 14:59:05 -0400 Subject: [PATCH 09/10] Fixed escape sequence. --- pep-0498.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pep-0498.txt b/pep-0498.txt index 79d482946..836ca77bf 100644 --- a/pep-0498.txt +++ b/pep-0498.txt @@ -213,7 +213,7 @@ 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 +f'\\u007b4*10}' is decoded to f'{4*10}', which evaluates as the integer 40:: >>> f'\u007b4*10}' From 617e3748a8d1105108db4accc03b32476b7482b4 Mon Sep 17 00:00:00 2001 From: "Eric V. Smith" Date: Fri, 28 Aug 2015 21:43:48 -0400 Subject: [PATCH 10/10] Added a section on the same expression being used multiple times in the same f-string. --- pep-0498.txt | 41 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 38 insertions(+), 3 deletions(-) diff --git a/pep-0498.txt b/pep-0498.txt index 836ca77bf..c29e6d913 100644 --- a/pep-0498.txt +++ b/pep-0498.txt @@ -640,10 +640,45 @@ having 2 expressions:: f'{x:.{width}}' -Expressions used multiple times -------------------------------- +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. + +For example, given:: + + >>> 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 ==========