[Python-Dev] PEP-498: Literal String Formatting

Eric V. Smith eric at trueblade.com
Mon Aug 10 03:05:45 CEST 2015


On 8/9/2015 9:02 PM, Eric V. Smith wrote:
> On 8/9/2015 8:24 PM, Peter Ludemann wrote:
>> What if logging understood lambda? (By testing for types.FunctionType).
>> This is outside PEP 498, but there might be some recommendations on how
>> "lazy" evaluation should be done and understood by some functions.
>>
>> e.g.:
>> log.info <http://log.info>(lambda: f'{foo} just did a {bar} thing')
>>
>> It's not pretty, but it's not too verbose. As far as I can tell, PEP 498
>> would work with this because it implicitly supports closures — that is,
>> it's defined as equivalent to
>> log.info <http://log.info>(lambda: ''.join([foo.__format__(), ' just did
>> a ', bar.__format__(), ' thing']))
>>
> 
> That basically works:
> class Foo:
>     def __init__(self, name):
>         self.name = name
> 
>     def __format__(self, fmt):
>         print(f'__format__: {self.name}')
>         return f'{self.name}'
> 
> 
> class Logger:
>     # accumulate log messages until flush is called
>     def __init__(self):
>         self.values = []
> 
>     def log(self, value):
>         self.values.append(value)
> 
>     def flush(self):
>         for value in self.values:
>             if callable(value):
>                 value = value()
>             print(f'log: {value}')
> 
> logger = Logger()
> 
> f1 = Foo('one')
> f2 = Foo('two')
> print('before log calls')
> logger.log('first log message')
> logger.log(lambda:f'f: {f1} {f2}')
> logger.log('last log message')
> print('after log calls')
> f1 = Foo('three')
> logger.flush()
> 
> 
> produces:
> 
> before log calls
> after log calls
> log: first log message
> __format__: three
> __format__: two
> log: f: three two
> log: last log message
> 
> 
> But note that when the lambdas are called, f1 is bound to Foo('three'),
> so that's what's printed. I don't think that's what the logging module
> would normally do, since it wouldn't see the rebinding.
> 
> I guess you'd have to change logging to do something special if it had a
> single argument which is a callable, or add new interface to it.
> 
> And of course you'd have to live with the ugliness of lambdas in the
> logging calls.
> 
> So, I can't say I'm a huge fan of the approach. But writing examples
> using f-strings is way more fun that using %-formatting or str.format!

Here's a better example that shows the closure. Same output as above:

class Foo:
    def __init__(self, name):
        self.name = name

    def __format__(self, fmt):
        print(f'__format__: {self.name}')
        return f'{self.name}'


class Logger:
    # accumulate log messages until flush is called
    def __init__(self):
        self.values = []

    def log(self, value):
        self.values.append(value)

    def flush(self):
        for value in self.values:
            if callable(value):
                value = value()
            print(f'log: {value}')

def do_something(logger):
    f1 = Foo('one')
    f2 = Foo('two')
    print('before log calls')
    logger.log('first log message')
    logger.log(lambda:f'f: {f1} {f2}')
    logger.log('last log message')
    print('after log calls')
    f1 = Foo('three')

logger = Logger()
do_something(logger)
logger.flush()




More information about the Python-Dev mailing list