PEP-498 updates: specify str.interpolate() and beef up some of the discussion text. Still needs work on the XXX marked sections.
This commit is contained in:
parent
eaf9ea6841
commit
2b710a1d86
120
pep-0498.txt
120
pep-0498.txt
|
@ -41,6 +41,9 @@ values. Some examples are::
|
||||||
>>> f'He said his name is {name!r}.'
|
>>> f'He said his name is {name!r}.'
|
||||||
"He said his name is 'Fred'."
|
"He said his name is 'Fred'."
|
||||||
|
|
||||||
|
This PEP proposes a new method on the str type: str.interpolate. This
|
||||||
|
method will be used to implement f-strings.
|
||||||
|
|
||||||
A similar feature was proposed in PEP 215 [#]_. PEP 215 proposed to
|
A similar feature was proposed in PEP 215 [#]_. PEP 215 proposed to
|
||||||
support a subset of Python expressions, and did not support the
|
support a subset of Python expressions, and did not support the
|
||||||
type-specific string formatting (the __format__ method) which was
|
type-specific string formatting (the __format__ method) which was
|
||||||
|
@ -139,6 +142,11 @@ order for this to work::
|
||||||
>>> outer(42)()
|
>>> outer(42)()
|
||||||
'x=42'
|
'x=42'
|
||||||
|
|
||||||
|
In addition, using locals() or globals() introduces an information
|
||||||
|
leak. A called routine that has access to the callers locals() or
|
||||||
|
globals() has access to far more information than needed to do the
|
||||||
|
string interpolation.
|
||||||
|
|
||||||
Guido stated [#]_ that any solution to better string interpolation
|
Guido stated [#]_ that any solution to better string interpolation
|
||||||
would not use locals() or globals().
|
would not use locals() or globals().
|
||||||
|
|
||||||
|
@ -185,21 +193,34 @@ Expressions cannot contain ':' or '!' outside of strings or parens,
|
||||||
brackets, or braces. The exception is that the '!=' operator is
|
brackets, or braces. The exception is that the '!=' operator is
|
||||||
special cased.
|
special cased.
|
||||||
|
|
||||||
|
str.interpolate()
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
str.interpolate will be a new method. It takes one argument: a mapping
|
||||||
|
of field names to values. This method is the same as str.format_map()
|
||||||
|
[#]_, with one difference: it does not interpret the field_name [#]_
|
||||||
|
in any way. The field name is only used to look up the replacement
|
||||||
|
value in the supplied mapping object. Like str.format() and
|
||||||
|
str.format_map(), it does interpret and apply the optional conversion
|
||||||
|
character and format specifier. Thus, a field name may not contain the
|
||||||
|
characters ':' or '}', nor the strings '!s' and '!r'.
|
||||||
|
|
||||||
Code equivalence
|
Code equivalence
|
||||||
----------------
|
----------------
|
||||||
|
|
||||||
The exact code that is executed when converting expressions to strings
|
An f-string is evaluated at run time as a call to str.interpolate().
|
||||||
is unspecified by this PEP. However, it is specified that once the
|
|
||||||
expression is evaluated, the result's __format__() method will be
|
|
||||||
called with the given format specifier.
|
|
||||||
|
|
||||||
For example, this code::
|
For example, this code::
|
||||||
|
|
||||||
f'abc{expr1:spec1}{expr2!r:spec2}def{expr3:!s}ghi'
|
f'abc{expr1:spec1}{expr2!r:spec2}def{expr3:!s}ghi'
|
||||||
|
|
||||||
May be evaluated as::
|
Will be be evaluated as::
|
||||||
|
|
||||||
|
'abc{expr1:spec1}{expr2!r:spec2}def{expr3:!s}ghi'.interpolate({'expr1': expr1, 'expr2': expr2, 'expr3': expr3})
|
||||||
|
|
||||||
|
Note that the string on which interpolate() is being called is
|
||||||
|
identical to the value of the f-string.
|
||||||
|
|
||||||
''.join(['abc', expr1.__format__('spec1'), repr(expr2).__format__('spec2'), 'def', str(expr3).__format__(''), 'ghi'])
|
|
||||||
|
|
||||||
Expression evaluation
|
Expression evaluation
|
||||||
---------------------
|
---------------------
|
||||||
|
@ -258,10 +279,11 @@ yields the value::
|
||||||
|
|
||||||
'ab10cstr< hi >de'
|
'ab10cstr< hi >de'
|
||||||
|
|
||||||
While the exact code that is executed when evaluating this f-string is
|
While the exact method of this runtime concatenation is unspecified,
|
||||||
not specified, one possible strategy is to evaluate::
|
the above code might evaluate to::
|
||||||
|
|
||||||
''.join(['ab', x.__format__(''), 'c', 'str<', y.__format__('^4'), '>', 'de'])
|
|
||||||
|
''.join(['ab', '{x}'.interpolate({'x': x}), 'c', 'str<', 'str<{y:^4}>'.interpolate({'y': y}), 'de'])
|
||||||
|
|
||||||
Error handling
|
Error handling
|
||||||
--------------
|
--------------
|
||||||
|
@ -310,24 +332,20 @@ or::
|
||||||
File "<stdin>", line 2, in <module>
|
File "<stdin>", line 2, in <module>
|
||||||
ValueError: Sign not allowed in string format specifier
|
ValueError: Sign not allowed in string format specifier
|
||||||
|
|
||||||
Leading whitespace in expressions is skipped
|
Leading and trailing whitespace in expressions is skipped
|
||||||
--------------------------------------------
|
---------------------------------------------------------
|
||||||
|
|
||||||
Because expressions may begin with a left brace ('{'), there is a
|
For ease of readability, leading and trailing whitespace in
|
||||||
problem when parsing such expressions. For example::
|
expressions is ignored. However, this does not affect the string or
|
||||||
|
keys passed to str.interpolate().
|
||||||
|
|
||||||
>>> f'{{k:v for k, v in [(1, 2), (3, 4)]}}'
|
>>> x = 100
|
||||||
'{k:v for k, v in [(1, 2), (3, 4)]}'
|
>>> f'x = { x }'
|
||||||
|
'x = 100'
|
||||||
|
|
||||||
In this case, the doubled left braces and doubled right braces are
|
This would be evaluated as::
|
||||||
interpreted as single braces, and the string becomes just a normal
|
|
||||||
string literal. There is no expression evaluation being performed.
|
|
||||||
|
|
||||||
To allow for expressions to begin with a left brace, whitespace
|
'x = { x }'.interpolate({' x ': 100})
|
||||||
characters at the beginning of an expression are skipped::
|
|
||||||
|
|
||||||
>>> f'{ {k:v for k, v in [(1, 2), (3, 4)]}}'
|
|
||||||
'{1: 2, 3: 4}'
|
|
||||||
|
|
||||||
Discussion
|
Discussion
|
||||||
==========
|
==========
|
||||||
|
@ -337,14 +355,15 @@ Most of the discussions on python-ideas [#]_ focused on a few issues:
|
||||||
- Whether to allow full Python expressions.
|
- Whether to allow full Python expressions.
|
||||||
- How to designate f-strings, and how to specify the location of
|
- How to designate f-strings, and how to specify the location of
|
||||||
expressions in them.
|
expressions in them.
|
||||||
- How to concatenate adjacent strings and f-strings.
|
|
||||||
|
|
||||||
XXX: more on the above issues.
|
XXX: more on the above issues.
|
||||||
|
|
||||||
Similar support in other languages
|
Similar support in other languages
|
||||||
----------------------------------
|
----------------------------------
|
||||||
|
|
||||||
XXX: Placeholder.
|
Wikipedia has a good discussion of string interpolation in other
|
||||||
|
programming languages [#]_. This feature is implemented in many
|
||||||
|
languages, with a variety of syntaxes and restrictions.
|
||||||
|
|
||||||
Differences between f-string and str.format expressions
|
Differences between f-string and str.format expressions
|
||||||
-------------------------------------------------------
|
-------------------------------------------------------
|
||||||
|
@ -377,6 +396,25 @@ use variables as index values::
|
||||||
See [#]_ for a further discussion. It was this observation that led to
|
See [#]_ for a further discussion. It was this observation that led to
|
||||||
full Python expressions being supported in f-strings.
|
full Python expressions being supported in f-strings.
|
||||||
|
|
||||||
|
Triple-quoted f-strings
|
||||||
|
-----------------------
|
||||||
|
|
||||||
|
Triple quoted f-strings are allowed. These strings are parsed just as
|
||||||
|
normal triple-quoted strings are. After parsing, the normal f-string
|
||||||
|
logic is applied, and str.interpolate() is called.
|
||||||
|
|
||||||
|
Raw f-strings
|
||||||
|
-------------
|
||||||
|
|
||||||
|
Raw and f-strings may be combined. For example they could be used to
|
||||||
|
build up regular expressions::
|
||||||
|
|
||||||
|
>>> header = 'Subject'
|
||||||
|
>>> fr'{header}:\s+'
|
||||||
|
'Subject:\\s+'
|
||||||
|
|
||||||
|
In addition, raw f-strings may be combined with triple-quoted strings.
|
||||||
|
|
||||||
No binary f-strings
|
No binary f-strings
|
||||||
-------------------
|
-------------------
|
||||||
|
|
||||||
|
@ -385,6 +423,8 @@ combine 'f' with 'b' string literals. The primary problem is that an
|
||||||
object's __format__() method may return Unicode data that is not
|
object's __format__() method may return Unicode data that is not
|
||||||
compatible with a bytes string.
|
compatible with a bytes string.
|
||||||
|
|
||||||
|
#XXX: maybe allow this, but encode the output as ascii?
|
||||||
|
|
||||||
!s and !r are redundant
|
!s and !r are redundant
|
||||||
-----------------------
|
-----------------------
|
||||||
|
|
||||||
|
@ -421,6 +461,27 @@ Lambdas may be used inside of parens::
|
||||||
>>> f'{(lambda x: x*2)(3)}'
|
>>> f'{(lambda x: x*2)(3)}'
|
||||||
'6'
|
'6'
|
||||||
|
|
||||||
|
Future extensions:
|
||||||
|
==================
|
||||||
|
|
||||||
|
XXX: By using another leading character (say, 'i'), we could extend
|
||||||
|
this proposal to cover internationalization and localization. The idea
|
||||||
|
is that the string would be passed to some lookup function before
|
||||||
|
.interpolate() is called on it:
|
||||||
|
|
||||||
|
>>> name = 'Eric'
|
||||||
|
>>> fi'Name: {name}'
|
||||||
|
|
||||||
|
Could be translated as::
|
||||||
|
|
||||||
|
gettext.gettext('Name: {name}').interpolate({'name': name})
|
||||||
|
|
||||||
|
If gettext.gettext() returned '{name} es mi nombre', then the
|
||||||
|
resulting string would be 'Eric es mi nombre'.
|
||||||
|
|
||||||
|
Any such internationalization work will be specified in an additional
|
||||||
|
PEP.
|
||||||
|
|
||||||
References
|
References
|
||||||
==========
|
==========
|
||||||
|
|
||||||
|
@ -445,6 +506,15 @@ References
|
||||||
.. [#] Avoid locals() and globals()
|
.. [#] Avoid locals() and globals()
|
||||||
(https://mail.python.org/pipermail/python-ideas/2015-July/034701.html)
|
(https://mail.python.org/pipermail/python-ideas/2015-July/034701.html)
|
||||||
|
|
||||||
|
.. [#] str.format_map() documentation
|
||||||
|
(https://docs.python.org/3/library/stdtypes.html#str.format_map)
|
||||||
|
|
||||||
|
.. [#] Format string syntax
|
||||||
|
(https://docs.python.org/3/library/string.html#format-string-syntax)
|
||||||
|
|
||||||
|
.. [#] Wikipedia article on string interpolation
|
||||||
|
(https://en.wikipedia.org/wiki/String_interpolation)
|
||||||
|
|
||||||
.. [#] Start of python-ideas discussion
|
.. [#] Start of python-ideas discussion
|
||||||
(https://mail.python.org/pipermail/python-ideas/2015-July/034657.html)
|
(https://mail.python.org/pipermail/python-ideas/2015-July/034657.html)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue