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
string formatting mechanisms.
f-strings provide a way to combine string literals with Python
expressions, using a minimal syntax. It should be noted that an
f-string is really an expression evaluated at run time, not a constant
value. An f-string is a string, prefixed with 'f', that contains
expressions inside braces. The expressions are replaced with their
values. Some examples are::
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', that
contains expressions inside braces. The expressions are replaced with
their values. Some examples are::
>>> import datetime
>>> name = 'Fred'
@ -110,11 +110,11 @@ inside strings.
In this sense, string.Template and %-formatting have similar
shortcomings to str.format(), but also support fewer formatting
options. In particular, they do not support __format__, so that there
is no way to control how a specific object is converted to a string,
nor can it be extended to additional types that want to control how
they are converted to strings (such as Decimal and datetime). This
example is not possible with string.Template::
options. In particular, they do not support the __format__ protocol,
so that there is no way to control how a specific object is converted
to a string, nor can it be extended to additional types that want to
control how they are converted to strings (such as Decimal and
datetime). This example is not possible with string.Template::
>>> value = 1234
>>> 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
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
would not use locals() or globals().
@ -214,20 +219,44 @@ special cased.
str.interpolate()
-----------------
str.interpolate(mapping) 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(), str.interpolate() does interpret and apply the
optional conversion and format_spec. Thus, a field_name may not
contain the characters ':' or '}', nor the strings '!s', '!r', or
'!a'.
str.interpolate(mapping) will be a new method on the str type. 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(), str.interpolate() does interpret
and apply the optional conversion and format_spec. Thus, a field_name
may not contain the characters ':' or '}', nor the strings '!s', '!r',
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
----------------
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::
@ -401,13 +430,77 @@ This would be evaluated as::
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.
- 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
----------------------------------
@ -528,13 +621,13 @@ Lambdas may be used inside of parens::
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
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}'
>>> i'Name: {name}'
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
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
(https://docs.python.org/3/library/stdtypes.html#printf-style-string-formatting)