Flushed out more text. Other than removing !s, !r, and !a, I think I am mostly done.

This commit is contained in:
Eric V. Smith 2015-08-21 18:35:55 -04:00
parent 1d6b74f376
commit 29a1f236da
1 changed files with 126 additions and 31 deletions

View File

@ -25,12 +25,12 @@ strings.
This PEP does not propose to remove or deprecate any of the existing This PEP does not propose to remove or deprecate any of the existing
string formatting mechanisms. string formatting mechanisms.
f-strings provide a way to combine string literals with Python f-strings provide a way to embed expressions inside string literals,
expressions, using a minimal syntax. It should be noted that an using a minimal syntax. It should be noted that an f-string is really
f-string is really an expression evaluated at run time, not a constant an expression evaluated at run time, not a constant value. In Python
value. An f-string is a string, prefixed with 'f', that contains source code, an f-string is a literal string, prefixed with 'f', that
expressions inside braces. The expressions are replaced with their contains expressions inside braces. The expressions are replaced with
values. Some examples are:: their values. Some examples are::
>>> import datetime >>> import datetime
>>> name = 'Fred' >>> name = 'Fred'
@ -110,11 +110,11 @@ inside strings.
In this sense, string.Template and %-formatting have similar In this sense, string.Template and %-formatting have similar
shortcomings to str.format(), but also support fewer formatting shortcomings to str.format(), but also support fewer formatting
options. In particular, they do not support __format__, so that there options. In particular, they do not support the __format__ protocol,
is no way to control how a specific object is converted to a string, so that there is no way to control how a specific object is converted
nor can it be extended to additional types that want to control how to a string, nor can it be extended to additional types that want to
they are converted to strings (such as Decimal and datetime). This control how they are converted to strings (such as Decimal and
example is not possible with string.Template:: datetime). This example is not possible with string.Template::
>>> value = 1234 >>> value = 1234
>>> f'input={value:#0.6x}' >>> f'input={value:#0.6x}'
@ -164,6 +164,11 @@ leak. A called routine that has access to the callers locals() or
globals() has access to far more information than needed to do the globals() has access to far more information than needed to do the
string interpolation. string interpolation.
If locals() and globals() were used, and if a future extension to this
PEP would add an internationalization layer before str.interpolate()
is called, a malicious translator could get access to additional
variables in the callers context.
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().
@ -214,20 +219,44 @@ special cased.
str.interpolate() str.interpolate()
----------------- -----------------
str.interpolate(mapping) will be a new method. It takes one argument: str.interpolate(mapping) will be a new method on the str type. It
a mapping of field names to values. This method is the same as takes one argument: a mapping of field names to values. This method is
str.format_map() [#]_, with one difference: it does not interpret the the same as str.format_map() [#]_, with one difference: it does not
field_name [#]_ in any way. The field_name is only used to look up the interpret the field_name [#]_ in any way. The field_name is only used
replacement value in the supplied mapping object. Like str.format() to look up the replacement value in the supplied mapping object. Like
and str.format_map(), str.interpolate() does interpret and apply the str.format() and str.format_map(), str.interpolate() does interpret
optional conversion and format_spec. Thus, a field_name may not and apply the optional conversion and format_spec. Thus, a field_name
contain the characters ':' or '}', nor the strings '!s', '!r', or may not contain the characters ':' or '}', nor the strings '!s', '!r',
'!a'. or '!a'.
Examples::
>>> name = 'Guido'
>>> 'name={name}'.interpolate({'name': name})
'name=Guido'
>>> '{date} was on a {date:%A}. It was {weather}.'.interpolate({'weather': 'sunny', 'date': datetime.date(1991, 10, 12)})
'1991-10-12 was on a Saturday. It was sunny.'
Like str.format_map(), only the field_name portion inside braces is
used to look up values in the mapping. The format_spec is not used as
part of this lookup. Thus::
>>> 'name={name:10}'.interpolate({'name': name})
'name=Guido '
But::
>>> 'name={name:10}'.interpolate({'name:10': name})
'name=Guido '
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: 'name'
Code equivalence Code equivalence
---------------- ----------------
An f-string is evaluated at run time as a call to str.interpolate(). An f-string is evaluated at run time using a call to str.interpolate().
For example, this code:: For example, this code::
@ -401,13 +430,77 @@ This would be evaluated as::
Discussion Discussion
========== ==========
Most of the discussions on python-ideas [#]_ focused on a few issues. python-ideas discussion
-----------------------
Most of the discussions on python-ideas [#]_ focused on three issues:
- How to denote f-strings,
- How to specify the location of expressions in f-strings, and
- Whether to allow full Python expressions. - Whether to allow full Python expressions.
- How to designate f-strings, and how to specify the location of
expressions in them.
XXX: more on the above issues. How to denote f-strings
***********************
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. This PEP
chose a leading 'f' character preceeding the string literal. This is
similar to how 'b' and 'r' prefixes change the meaning of the string
itself, at compile time. Other prefixes were suggested, such as 'i'. No
option seemed better than the other, so 'f' was chosen.
Another option was to support special functions, known to the
compiler, such as Format(). This seems like too much magic for Python:
not only is there a chance for collision with existing identifiers,
the PEP author feels that it's better to signify the magic with a
string prefix character.
How to specify the location of expressions in f-strings
*******************************************************
This PEP supports the same syntax as str.format() for distinguishing
replacement text inside strings: expressions are contained inside
braces. There were other options suggested, such as string.Template's
$identifier or ${expression}.
While $identifier is no doubt more familiar to shell scripters and
users of some other languages, in Python str.format() is heavily
used. A quick search of Python's standard library shows only a handful
of uses of string.Template, but over 1000 uses of str.format().
Another proposed alternative was to have the substituted text between
\{ 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
text, in order to leverage end user familiarity with str.format().
Supporting full Python expressions
**********************************
Many people on the python-ideas discussion wanted support for either
only single identifiers, or a limited subset of Python expressions
(such as the subset supported by str.format()). This PEP supports full
Python expressions inside the braces. Without full expressions, some
desirable usage would be forbidden::
>>> f'Column={col_idx+1}'
>>> f'number of items: {len(items)}'
would become::
>>> col_number = col_idx+1
>>> f'Column={col_number}'
>>> n_items = len(items)
>>> f'number of items: {n_items}'
While it's true that very ugly expressions could be included in the
f-strings, this PEP takes the position that such uses should be
addressed in a linter or code review::
>>> f'mapping is { {a:b for (a, b) in ((1, 2), (3, 4))}}'
'mapping is {1: 2, 3: 4}'
Similar support in other languages Similar support in other languages
---------------------------------- ----------------------------------
@ -528,13 +621,13 @@ Lambdas may be used inside of parens::
Future extensions: Future extensions:
================== ==================
XXX: By using another leading character (say, 'i'), we could extend By using another leading character (say, 'i'), we could extend this
this proposal to cover internationalization and localization. The idea proposal to cover internationalization and localization. The idea is
is that the string would be passed to some lookup function before that the string would be passed to some lookup function before
.interpolate() is called on it:: .interpolate() is called on it::
>>> name = 'Eric' >>> name = 'Eric'
>>> fi'Name: {name}' >>> i'Name: {name}'
Could be translated as:: Could be translated as::
@ -551,8 +644,10 @@ field_names are not found in the mapping argument. The choices might
be: use the field_name, use a default (possibly empty) string, or be: use the field_name, use a default (possibly empty) string, or
raise an exception. raise an exception.
References There would obviously need to be some way to specify to the compiler
========== what lookup function would be called.
References ==========
.. [#] %-formatting .. [#] %-formatting
(https://docs.python.org/3/library/stdtypes.html#printf-style-string-formatting) (https://docs.python.org/3/library/stdtypes.html#printf-style-string-formatting)