Python string interpolation trick
December 7th, 2008Today, I will show you a trick that may help you with the heavier string formatting jobs in Python.
Most people using Python know how to format output using string interpolation. It works much like C’s sprintf
, except for the funny %
operator:
>>> a = "the Knights" >>> b = "Ni" >>> "we are %s who say %s" % (a, b) 'we are the Knights who say Ni'
What is less known is that you can replace the right hand argument by a dictionary and use its keys to tell what value goes where. That’s handy when you need to insert more than one or two in a string, or when a single value is used more than once. Especially for localisation strings, it gives you the flexibility to determine the when and where of the values in the string itself.
>>> "we are %(who)s who say %(what)s" % {"who": a, "what": b} 'we are the Knights who say Ni'
Combine that with the even lesser known function vars()
which returns a dictionary of the local variables and their values:
>>> vars() {'__builtins__': <module '__builtin__' (built-in)>, '__name__': '__main__', 'b': 'Ni', '__doc__': None, 'a': 'the Knights'}
Use it at the right hand, and you can simply use the variable names as keywords:
>>> "we are %(a)s who say %(b)s" % vars() 'we are the Knights who say Ni'
This probably is the closest you can get to the Perl and PHP style syntax "we are the $a who say $b"
.
Share and Enjoy!
November 17th, 2009 at 2:19 am
Hoi,
Used this technique to make a evaluation function for record processing.
1. write evaluation function as string interpollation, use field names as vars
2. feed field list
3. feed value list
Need to use str() for correct string processing in the eval function…
def eval_vars(eval_string, fields, rec, echo = False):
# evaluate expression
d = {}
for f, x in zip(fields, rec):
d[f]=x
e_string = eval_string % d
if echo:
print e_string, eval(e_string)
try:
return eval(e_string)
except:
return False
a = [‘POSTCODE’, ‘WAARDE’,’INTEGER’]
b = [‘9000’ , 45.23, 17]
c = [‘9315’, 65.2 , 17]
d = [‘9031’, 62.5 , 23]
e = [‘9031’, 62.5 , 17]
eval_vars(‘%(WAARDE)f > 50.0’, a, b, True)
eval_vars(‘”%(POSTCODE)s”[:2] == str(90)’, a, b, True)
eval_vars(‘str(%(POSTCODE)s) == str(9000)’, a, b, True)
eval_vars(‘str(%(POSTCODE)s)[:2] == str(90)’, a, b, True)
eval_vars(‘%(INTEGER)i == 17’, a, b, True)
eval_vars(‘%(INTEGER)i == 17 and str(%(POSTCODE)s)[:2] == str(90)’, a, c, True)
eval_vars(‘(%(INTEGER)i in range(5,16) or %(INTEGER)i in range(20,25)) and str(%(POSTCODE)s)[:2] == str(90)’, a, d, True)
eval_vars(‘(%(INTEGER)i in range(5,16) or %(INTEGER)i in range(20,25)) and str(%(POSTCODE)s)[:2] == str(90)’, a, e, True)
I can write any query on any dataset now…
Feeding a dictionary would be shorter, but loading large datasets as lists saves a lot of time and memory.
Thanks for the idea….
Luc
November 21st, 2009 at 8:05 am
Hi Luc,
You’re welcome. That’s neat application you’ve shown there. Here’s a few ideas on taking this a little bit further.
disclaimer: none of the following ideas use string interpolation so it is a bit off-topic =)
You can get a cleaner syntax in your predicate strings by feeding
d
directly to theeval
function. That way you can use your field names as identifiers ineval_string
. Like so:You might notice another small improvement in building
d
itself.dict
accepts a sequence of (key, value) pairs, so you canzip
the field names and the valules.Another way of doing all this, is using lambda expressions instead of strings. This might be cleaner if you are embedding the predicates in the python code, but you can’t pull them from let’s say a text box anymore. Constructing
d
becomes somewhat harder, as you must only keep the fields that are really arguments.Cheers,
Bram