[Python-ideas] Draft PEP on string interpolation

Ron Adam ron3200 at gmail.com
Wed Aug 26 04:20:42 CEST 2015


On 08/24/2015 09:42 PM, Eric V. Smith wrote:
>> On Aug 24, 2015, at 10:23 PM, Ron
>> Adam<ron3200 at gmail.com>  wrote:
>>>
>>> On 08/24/2015 06:45 PM, Mike Miller wrote:
>>>>>>> - How problematic will it be that an e-string pins all
>>>>>>> the interpolated objects in memory for its lifetime?
>>>>>
>>>>> It will be an object holding a raw template string, and a
>>>>> number of variables. In normal usage I don't suspect it to be
>>>>> a problem.
>>>
>>> If an objects __str__ method could have an optional fmt='spec'
>>> argument, then an estring, could just hold strings, and not the
>>> object references.  That also prevent surprises if the object is
>>> mutated between the time it's estring is created and when the
>>> estring is used as a string.  For that matter it prevents an
>>> estring from printing one way at one time, and another at another
>>> time.
>>>
>>> I don't know if the fomatting can be split like this...  Where an
>>> object is formatted to a string representation, and then that is
>>> formatted to a field specification.   The later being things like
>>> width, fill, right, center, and left.   These are independent of
>>> the object and belong to the string.  Things like nubmer of
>>> places and sign or to use leading or trailing zeros is part of
>>> the object being converted to a string.

> It's not possible. For examples, look at all of the number format
> options. How would you implement hex conversions? Or datetime %A?

I'm not sure which part you are referring to..  But I think adding an 
optional argument to __str__ methods is probably out.

As to splitting the format spec, I think it would be possible, but It 
may not be needed.

I still think early evaluation is a must here.  The issue I have with 
the late evaluation is shown in your current example of logging.  If the 
time which may be from an actual time() function rather than a fixed 
time is not evaluated until the logged list is printed at the end of the 
run, all the times will be set to when it's printed rather than when the 
logged even happened.

Another similar reason is the evaluated expression is sensitive to what 
object is in the name at the time it is evaluated.  If it's evaluated 
later, the object from the name look up may be something entirely 
unexpected because that name may have been reused during each iteration 
of a loop.  So all the logged entries that refer to that name will give 
the last value rather than the value at the time the event was logged.


Here's a slightly reworked version to compare to.

Hope this is helpful,
   Ron



import sys
import _string

def interleave(*iters):
     result = []
     for items in zip(*iters):
         for item in items:
             result.append(item)
     return result


# i-string
class i:
     def __init__(self, s):
         self.s = s
         locals = sys._getframe(1).f_locals
         globals = sys._getframe(1).f_globals
         self.literals = []
         self.values = []
         # Evaluate the expressions now, and remember them.
         # This freezes the value at execution time.
         for literal, expr, format_spec, conversion in \
                 _string.formatter_parser(self.s):
             self.literals.append(literal)
             if expr:
                 value = eval(expr, locals, globals)
                 self.values.append(value.__format__(format_spec))
             else:
                 self.values.append('')

     def __str__(self):
         return ''.join(interleave(self.literals, self.values))



# f-string
def f(s):
     return str(i(s))


# logging
def log(istring, echo=True):
     logged = 'log:' + str(istring)
     print(logged)
     return logged



# test

if __name__ == '__main__':

     x = i('Version in caps {sys.version.upper()!r}')
     print(str(x))


     name = 'Eric'
     dog = 'Fido'
     s = f('My name is {name}, my dog is {dog}')
     print(repr(s))
     assert repr(s) == "'My name is Eric, my dog is Fido'"
     assert type(s) == str


     import datetime
     def func(value):
         return i('called func with "{value:10}"')

     logline = 'as of {now:%Y-%m-%d} the value is {400+1:#06x}'
     now = datetime.datetime(2015, 8, 10, 12, 13, 15)
     logged = log(i(logline), echo=True)
     assert logged == "log:as of 2015-08-10 the value is 0x0191"

     now = datetime.datetime(2015, 8, 11, 12, 13, 15)
     logged = log(i(logline), echo=True)
     assert logged == "log:as of 2015-08-11 the value is 0x0191"

     logged = log(i('{func(42)}'))
     assert logged == 'log:called func with "        42"'


     import re
     delimiter = '+'
     trailing_re = re.escape(r'\S+')
     regex = i(r'{delimiter}\d+{delimiter}{trailing_re}')
     print(regex)
     assert str(regex) == r"+\d++\\S\+"



















More information about the Python-ideas mailing list