many times, templating a string is a tidious task. using the % operator, either with tuples or dicts,<br>is difficult to maintain, when the number of templated arguments is large. and string.Template,<br>although more easy to read, is less intutive and cumbersome:
<br><br>import string<br>t = string.Template(&quot;hello $name&quot;)<br>print t.substitute({&quot;name&quot; : &quot;john&quot;})<br><br>as you can see, it is is redundant, as you must repeat the dict keys in two places, and imagine 
<br>maintaining such a template with 20 parameters! if you change the one argument's name in the <br>template, you must go and fix all locations that use that template. not nice at all.<br><br>i'm suggesting something like boo's string interpolation: 
<a href="http://boo.codehaus.org/String+Interpolation">http://boo.codehaus.org/String+Interpolation</a><br>but i chose to call it &quot;evaluated string&quot;.<br><br>like raw strings (r&quot;&quot;), which are baiscally a syntactic sugar, evaluated strings will be marked by 'e',
<br>for instance, e&quot;&quot;, which may be combined with the 'r' or 'u', that exist today.<br><br>the evaluated string will be evaluated based on the current scope (locals and globals), just like<br>normal expressions. the difference is, the results of the expressions will be str()ed into the 
<br>evaluated string directly. these expressions will be embedded into the string surrounded by<br>special delimiters, and an unclosed delimited or a syntax error will be reported at just like &quot;\x??&quot;<br>raises &quot;ValueError: invalid \x escape&quot;.
<br><br>i'm not sure which delimiters to use, but i think only { } is sufficient (no need for ${ } like in boo)<br><br>some examples:<br>===============<br>name = &quot;john&quot;<br>print e&quot;hello {name}&quot;<br><br>
a = 3<br>b = 7<br>print e&quot;the function is y = {a}x + {b}&quot;<br>for x in range(10):<br>&nbsp;&nbsp;&nbsp; print e&quot;y({x}) = {a*x+b}&quot;<br><br>import time, sys<br>print e&quot;the time is {time.asctime()} and you are running on {
sys.platform}&quot;<br>===============<br><br>in order to implement it, i suggest a new type, estr. doing <br>a = e&quot;hello&quot;<br>will be equivalent to <br>a = estr(&quot;hello&quot;, locals(), globals()), <br>just like
<br>u&quot;hello&quot;<br>is equivalent to<br>unicode(&quot;hello&quot;) <br>(if we ignore \u escaping for a moment)<br><br>and just like unicode literals introduce the \u escape, estr literals would introduce \{ and \} <br>
to escape delimiters.<br><br>the estr object will be evaluated with the given locals() and globals() only at __repr__ or __str__,<br>which means you can work with it like a normal string:<br><br>a = e&quot;hello {name} &quot; 
<br>b = e&quot;how nice to meet you at this lovely day of {time.localtime().tm_year}&quot;<br>c = a + b<br>c is just the concatenation of the two strings, and it will will be evaluated as a whole when you<br>str()/repr() it. of course the internal representation of the object shouldnt not as a string,
<br>rather a sequence of static (non evaluated) and dynamic (need evaluation) parts, i.e.:<br>[&quot;hello&quot;, &quot;name&quot;, &quot;how nice to meet you at this lovely day of&quot;, &quot;time.localtime().tm_year&quot;],
<br>so evaluating the string will be fast (just calling eval() on the relevant parts)<br><br>also,&nbsp; estr objects will not support getitem/slicing/startswith, as it's not clear what the indexes are...<br>
you'd have to first evaluate it and then work with the string:<br>str(e&quot;hello&quot;)[2:]<br><br>estr will have a counterpart type called eunicode. some rules:<br>estr + str =&gt; estr<br>estr + estr =&gt; estr<br>estr + unicode =&gt; eunicode
<br>estr + eunicode =&gt; eunicode<br>eunicode + eunicode =&gt; eunicode<br><br>there are no backwards compatibility issues, as e&quot;&quot; is an invalid syntax today, and as for clarity, <br>i'm sure editors like emacs and the like can be configured to highlight the strings enclosed by {} 
<br>like normal expressions.<br><br>i know it may cause the perl-syndrome, where all the code of the program is pushed into strings,<br>but templating/string interpolation is really a fundamental requirement of scripting languages,
<br>and the perl syndrome can be prevented with two precautions:<br>* compile the code with &quot;eval&quot; flag instead of &quot;exec&quot;. this would prevent abominations like<br>e&quot;{import time\ndef f(a):\n\tprint 'blah'}&quot;
<br>* do not allow the % operator to work on estr's, to avoid awful things like<br>e&quot;how are %s {%s}&quot; % (&quot;you&quot;, &quot;name&quot;)<br>one templating mechanism at a time, please :)<br><br>perhaps there are other restrictions to impose, but i couldnt think of any at the moment. 
<br><br>here's a proposed implementation:<br><br>class estr(object): # can't derive from basestring!<br>&nbsp;&nbsp;&nbsp; def __init__(self, raw, locals, globals):<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; self.elements = self._parse(raw)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; self.locals = locals
<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; self.globals = globals<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; def _parse(self, raw):<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; i = 0<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; last_index = 0<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; nesting = 0<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; elements = []<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; while i &lt; len(raw):<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if raw[i] == &quot;{&quot;:
<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if nesting == 0:<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; elements.append((False, raw[last_index : i]))<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; last_index = i + 1<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; nesting += 1<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; elif raw[i] == &quot;}&quot;:
<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; nesting -= 1<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if nesting == 0:<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; elements.append((True, raw[last_index : i]))<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; last_index = i + 1<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if nesting &lt; 0:<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; raise ValueError(&quot;too many '}' (at index %d)&quot; % (i,))
<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; i += 1<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if nesting &gt; 0:<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; raise ValueError(&quot;missing '}' before end&quot;)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if last_index &lt; i:<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; elements.append((False, raw[last_index : i]))<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return elements
<br>&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; def __add__(self, obj):<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if type(obj) == estr:<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; elements = self.elements + obj.elements<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else:<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; elements = self.elements + [(False, obj)]<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; # the new&nbsp; object inherits the current one's namespace (?)
<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; newobj = estr(&quot;&quot;, self.locals, self.globals)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; newobj.elements = elements<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return newobj<br>&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; def __mul__(self, count):<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; newobj = estr(&quot;&quot;, self.locals
, self.globals)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; newobj.elements = self.elements * count<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return newobj<br>&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; def __repr__(self):<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return repr(self.__str__())<br>&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; def __str__(self):<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; result = []
<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for dynamic, elem in self.elements:<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if dynamic:<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; result.append(str(eval(elem, self.locals, self.globals)))<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else:<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; result.append(str(elem))<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return &quot;&quot;.join(result)
<br><br>myname = &quot;tinkie winkie&quot;<br>yourname = &quot;la la&quot;<br>print estr(&quot;{myname}&quot;, locals(), globals())<br>print estr(&quot;hello {myname}&quot;, locals(), globals())<br>print estr(&quot;hello {yourname}, my name is {myname}&quot;, locals(), globals())
<br><br>a = 3<br>b = 7<br>print estr(&quot;the function is y = {a}x + {b}&quot;, locals(), globals())<br>for x in range(10):<br>&nbsp;&nbsp;&nbsp; print estr(&quot;y({x}) = {a*x+b}&quot;, locals(), globals())<br><br>a = estr(&quot;hello {myname}&quot;, locals(), globals())
<br>b = estr(&quot;my name is {myname} &quot;, locals(), globals())<br>c = a + &quot;, &quot; + (b * 2)<br>print c<br>======<br><br>the difference is that when __str__ is called, it will figure out by itself the locals() and globals()
<br>using the stack frame or whatever.<br><br><br><br>-tomer