f-string anomaly

Thomas Jollans tjol at tjol.eu
Mon May 14 06:15:36 EDT 2018


On 2018-05-14 04:08, Terry Reedy wrote:
> On 5/13/2018 3:22 PM, Ken Kundert wrote:
> 
> Please do not double post.
> 
>> I am seeing an unexpected difference between the behavior of the string
>> format method and f-strings.
> 
> Read
> https://docs.python.org/3/reference/lexical_analysis.html#formatted-string-literals
> carefully.
> 
>> Here is an example:
>>
>>      import sys, os
>>      from inform import error, os_error
>>
>>      class mydict(dict):
>>          def __format__(self, template):
>>              print('Template:', template)
>>              return ', '.join(template.format(v, k=k, v=v) for k, v in
>> self.items())
>>
>>
>>      d = mydict(bob='239-8402', ted='371-8567', carol='891-5810',
>> alice='552-2219')
>>
>>      print('Using format():')
>>      print('Email: {0:{{k}}: {{v}}}'.format(d))
>>      print()
>>      print('Using f-string:')
>>      print(f'Email: {d:{{k}} {{v}}}')
>>      print()
>>      print('Using f-string:')
>>      print(f'Email: {d:{{k}} {{v}}}', k=6, v=9)
>>
>>
>> It generates the following response:
>>
>>      Using format():
>>      Template: {k}: {v}
>>      Email: bob: 239-8402, ted: 371-8567, carol: 891-5810, alice:
>> 552-2219
>>
>>      Using f-string:
>>      Traceback (most recent call last):
>>      File "tryit", line 18, in <module>
>>          print(f'Email: {d:{{k}} {{v}}}')
>>      NameError: name 'k' is not defined
> 
> This is what I expected.
> 
>> Essentially I am using a format string as the template that indicates
>> how to format each member of a dictionary, {{k}} should interpolate the
>> key and {{v}} interpolates the value.  This format string is embedded
>> inside another format string, so the braces are doubled up so that they
>> will be ignored by the outer format string.
> 
> "The parts of the string outside curly braces are treated literally,
> except that any doubled curly braces '{{' or '}}' are replaced with the
> corresponding single curly brace. " note 'outside'

It only accidentally works with the format method. It, too, does not
support escaped curly brackets in format specifiers, but they'll slip
through IFF all of them are matched:

>>> d = mydict(bob='239-8402', ted='371-8567', carol='891-5810',
... alice='552-2219')
>>> 'Email: {0:{{k}}: {{v}}}'.format(d)
Template: {k}: {v}
'Email: bob: 239-8402, ted: 371-8567, carol: 891-5810, alice: 552-2219'
>>> 'Email: {0:{{k}} {{{{ {{v}}}'.format(d)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: unmatched '{' in format spec
>>> 'Email: {0:{{k}} {{{{}}}} {{v}}}'.format(d)
Template: {k} {{}} {v}
'Email: bob {} 239-8402, ted {} 371-8567, carol {} 891-5810, alice {}
552-2219'
>>>


> 
>> This idea seems to work okay when using the format() method. You can see
>> I added a print statement inside __format__ that shows that the method
>> is being called.
>>
>> However, trying the same idea with f-strings results in a NameError.  It
>> appears that the escaping does not work when used within the template.
>> It appears the error occurs before __format__ is called (there is no
>> output from the print function).
>>
>> Does anybody know why the format() method would work in this case but
>> the f-string would not?
> 
> All names in the expression are resolved in the local namespace of the f
> string.  There are other differences.  Nesting can only be one level deep.
> 
>> Is this a bug in f-strings?
> 
> Not to me.
> 
> 




More information about the Python-list mailing list