[Python-Dev] Alternative Implementation for PEP 292: Simple String Substitutions (original) (raw)

Raymond Hettinger raymond.hettinger at verizon.net
Thu Aug 26 23:38:56 CEST 2004


Before it's too late and the API gets frozen, I would like to propose an alternate implementation for PEP292 that exposes two functions instead of two classes.

Current way: print Template('Turn $direction') % dict(direction='right')

Proposed: print dollarsub('Turn $direction', dict(direction='right')) or: print dollarsub('Turn $direction', direction='right')

My main issue with the current implementation is that we get no leverage from using a class instead of a function. Though the API is fairly simple either way, it is easier to learn and document functions than classes. We gain nothing from instantiation -- the underlying data remains immutable and no additional state is attached. The only new behavior is the ability to apply the mod operator. Why not do it in one step.

I had thought a possible advantage of classes was that they could be usefully subclassed. However, a little dickering around showed that to do anything remotely interesting (beyond changing the pattern alphabet) you have to rewrite everything by overriding both the method and the pattern. Subclassing gained you nothing, but added a little bit of complexity. A couple of simple exercises show this clearly: write a subclass using a different escape character or one using dotted identifiers for attribute lookup in the local namespace -- either way subclasses provide no help and only get in the way.

One negative effect of the class implementation is that it inherits from unicode and always returns a unicode result even if all of the inputs (mapping values and template) are regular strings. With a function implementation, that can be avoided (returning unicode only if one of the inputs is unicode).

The function approach also makes it possible to have keyword arguments (see the example above) as well as a mapping. This isn't a big win, but it is nice to have and reads well in code that is looping over multiple substitutions (mailmerge style):

for girl in littleblackbook: print dollarsub(loveletter, name=girl[0].title(), favoritesong=girl[3])

Another minor advantage for a function is that it is easier to lookup in the reference. If a reader sees the % operator being applied and looks it up in the reference, it is going to steer them in the wrong direction. This is doubly true if the Template instantiation is remote from the operator application.

Summary for functions:

Raymond

----------- Sample Implementation -------------

def dollarsub(template, mapping=None, **kwds): """A function for supporting $-substitutions.""" typ = type(template) if mapping is None: mapping = kwds def convert(mo): escaped, named, braced, bogus = mo.groups()
if escaped is not None: return '$' if bogus is not None: raise ValueError('Invalid placeholder at index %d' % mo.start('bogus')) val = mapping[named or braced] return typ(val) return _pattern.sub(convert, template)

def safedollarsub(template, mapping=None, **kwds): """A function for $-substitutions.

This function is 'safe' in the sense that you will never get

KeyErrors if there are placeholders missing from the interpolation dictionary. In that case, you will get the original placeholder in the value string. """ typ = type(template) if mapping is None: mapping = kwds
def convert(mo): escaped, named, braced, bogus = mo.groups() if escaped is not None: return '$' if bogus is not None: raise ValueError('Invalid placeholder at index %d' % mo.start('bogus')) if named is not None: try: return typ(mapping[named]) except KeyError: return '$' + named try: return typ(mapping[braced]) except KeyError: return '${' + braced + '}' return _pattern.sub(convert, template)



More information about the Python-Dev mailing list