By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
455,533 Members | 1,334 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 455,533 IT Pros & Developers. It's quick & easy.

String handling and the percent operator

P: n/a
I have some code to autogenerate some boilerplate code so that I don't
need to do the tedious setup stuff when I want to create a new module.

So, my script prompts the user for the module name, then opens two
files and those files each get the contents of one of these functions:

def GetPyContents(module):
boilerplate = \
"""
class %s:
pass

if __name__ == '__main__':
import unittest
unittest.main('%s_t')
"""

return boilerplate % ((module,) * 2)

def GetTestContents(module):
boilerplate = \
"""from %s import *
import unittest

class Test%s(unittest.TestCase):
def testConstruction(self):
self.failUnless(%s())

def testWriteMoreTests(self):
self.fail('This test should fail.')

if __name__ == '__main__':
unittest.main()
"""

return boilerplate % ((module,) * 3)

My question is, I don't like hardcoding the number of times that the
module name should be repeated in the two return functions. Is there
an straight forward (inline-appropriate) way to count the number of
'%s'es in the 'boilerplate' strings? ...or maybe a different and more
Pythonic way to do this? (Maybe I could somehow use generators?)

thx.
-tom!
Jul 13 '06 #1
Share this Question
Share on Google+
11 Replies


P: n/a
Tom Plunket wrote:
I have some code to autogenerate some boilerplate code so that I don't
need to do the tedious setup stuff when I want to create a new module.

So, my script prompts the user for the module name, then opens two
files and those files each get the contents of one of these functions:

def GetPyContents(module):
boilerplate = \
"""
class %s:
pass

if __name__ == '__main__':
import unittest
unittest.main('%s_t')
"""

return boilerplate % ((module,) * 2)

def GetTestContents(module):
boilerplate = \
"""from %s import *
import unittest

class Test%s(unittest.TestCase):
def testConstruction(self):
self.failUnless(%s())

def testWriteMoreTests(self):
self.fail('This test should fail.')

if __name__ == '__main__':
unittest.main()
"""

return boilerplate % ((module,) * 3)

My question is, I don't like hardcoding the number of times that the
module name should be repeated in the two return functions. Is there
an straight forward (inline-appropriate) way to count the number of
'%s'es in the 'boilerplate' strings? ...or maybe a different and more
Pythonic way to do this? (Maybe I could somehow use generators?)

thx.
-tom!

strings have a count() method.

Since you know that you won't have things like '%%s' in your
boilerplate, it's perfectly reasonable to use:

return boilerplate % ((module,) * boilerplate.count('%s'))

in your code.

Peace,
~Simon

return boilerplate % ((module,) * 3)

Jul 13 '06 #2

P: n/a
Simon Forman wrote:
strings have a count() method.
thanks!

For enrichment purposes, is there a way to do this sort of thing with
a generator? E.g. something like:

def SentenceGenerator():
words = ['I', 'have', 'been', 'to', 'the', 'fair']
for w in words:
yield w

message = "%s %s %s %s"

print message % SentenceGenerator()

(I ask because the above doesn't work)?
-tom!
Jul 13 '06 #3

P: n/a
Tom Plunket wrote:
For enrichment purposes, is there a way to do this sort of thing with
a generator? E.g. something like:

def SentenceGenerator():
words = ['I', 'have', 'been', 'to', 'the', 'fair']
for w in words:
yield w

message = "%s %s %s %s"

print message % SentenceGenerator()

(I ask because the above doesn't work)?
Use tuple(SentenceGenerator()). A generator is just another object, so
using it with the % operator tries to substitute it at one value. (Even
with this fix, though, your message didn't have enough formatters.)

--
Erik Max Francis && ma*@alcyone.com && http://www.alcyone.com/max/
San Jose, CA, USA && 37 20 N 121 53 W && AIM erikmaxfrancis
Human salvation lies in the hands of the creatively maladjusted.
-- Dr. Martin Luther King, Jr.
Jul 13 '06 #4

P: n/a
Tom Plunket wrote:
boilerplate = \
"""
[big string]
"""

return boilerplate % ((module,) * 3)

My question is, I don't like hardcoding the number of times that the
module name should be repeated in the two return functions. Is there
an straight forward (inline-appropriate) way to count the number of
'%s'es in the 'boilerplate' strings? ...or maybe a different and more
Pythonic way to do this? (Maybe I could somehow use generators?)

thx.
-tom!
Of course..
>>stuff = {'lang': 'python', 'page': 'typesseq-strings.html'}
print """I should read the %(lang)s documentation at
.... http://docs.%(lang)s.org/lib/%(page)s""" % stuff
I should read the python documentation at
http://docs.python.org/lib/typesseq-strings.html
--
- Justin

Jul 14 '06 #5

P: n/a
Justin Azoff wrote:
Tom Plunket wrote:
> boilerplate = \
"""
[big string]
>"""

return boilerplate % ((module,) * 3)
[deletia...]
Of course..
>>>stuff = {'lang': 'python', 'page': 'typesseq-strings.html'}
print """I should read the %(lang)s documentation at
... http://docs.%(lang)s.org/lib/%(page)s""" % stuff
I should read the python documentation at
http://docs.python.org/lib/typesseq-strings.html
....or perhaps cooler:
http://docs.python.org/lib/node109.html

something like:

from string import Template

def GetPyContents(module):
boilerplate = Template( \
"""
class $moduleName:
pass

if __name__ == '__main__':
import unittest
unittest.main('${moduleName}_t')
""")

return boilerplate.substitute(moduleName=module)
Jul 14 '06 #6

P: n/a
Tom Plunket wrote:
Simon Forman wrote:
strings have a count() method.

thanks!

For enrichment purposes, is there a way to do this sort of thing with
a generator? E.g. something like:

def SentenceGenerator():
words = ['I', 'have', 'been', 'to', 'the', 'fair']
for w in words:
yield w

message = "%s %s %s %s"

print message % SentenceGenerator()

(I ask because the above doesn't work)?
-tom!
If you're asking if there's some way that the generator can know how
many formatting fields are in message then the straightforward answer
is no. At least not without passing the message to the generator for
it to call count() itself, and then you're better off just doing it
without the generator.

(Actually, IIRC, this was discussed quite recently on this list, and
(again IIRC) I think there is some deep voodoo that can do this, but
you're much better off without it.)

FWIW, in your shoes I would use the trick Justin Azoff posted, i.e.:

boiler = 'foo %(modname)s bar %(modname)s baz'

message = boiler % {'modname': modname}
Peace,
~Simon

Jul 14 '06 #7

P: n/a
Tom Plunket wrote:
I have some code to autogenerate some boilerplate code so that I don't
need to do the tedious setup stuff when I want to create a new module.

So, my script prompts the user for the module name, then opens two
files and those files each get the contents of one of these functions:

def GetPyContents(module):
boilerplate = \
"""
class %s:
pass

if __name__ == '__main__':
import unittest
unittest.main('%s_t')
"""

return boilerplate % ((module,) * 2)

def GetTestContents(module):
boilerplate = \
"""from %s import *
import unittest

class Test%s(unittest.TestCase):
def testConstruction(self):
self.failUnless(%s())

def testWriteMoreTests(self):
self.fail('This test should fail.')

if __name__ == '__main__':
unittest.main()
"""

return boilerplate % ((module,) * 3)

My question is, I don't like hardcoding the number of times that the
module name should be repeated in the two return functions. Is there
an straight forward (inline-appropriate) way to count the number of
'%s'es in the 'boilerplate' strings? ...or maybe a different and more
Pythonic way to do this? (Maybe I could somehow use generators?)
Python's string formatting comes in two flavours : positional (the one
you used in your above example), and keywords:

tpls = [
"%(name1)s is %(name1)s and %(name2)s is %(name2)s",
"what about %(name2)s for %(name1)s ?",
"Now we only deal with %(name1)s",
]

data = {'name1' : 'parrot', 'name2': 'dead'}

for tpl in tpls:
print tpl % data

As you can see, the keyword flavour doesn't care about positions nor
repetitions.

You may also want to look at more featured templating solutions like
empy, cheetah, etc...

--
bruno desthuilliers
python -c "print '@'.join(['.'.join([w[::-1] for w in p.split('.')]) for
p in 'o****@xiludom.gro'.split('@')])"
Jul 14 '06 #8

P: n/a
Justin Azoff wrote:
Of course..

I should read the python documentation at
http://docs.python.org/lib/typesseq-strings.html
Excellent. Thanks. Has this been around long? I "learned" Python in
the 1.6 days iirc, but haven't done much except simple scripting with
it since...
-tom!
Jul 14 '06 #9

P: n/a
Tom Plunket wrote:
Excellent. Thanks. Has this been around long? I "learned" Python in
the 1.6 days iirc, but haven't done much except simple scripting with
it since...
Yep. Been around since at least 1.5.x.

--
Erik Max Francis && ma*@alcyone.com && http://www.alcyone.com/max/
San Jose, CA, USA && 37 20 N 121 53 W && AIM erikmaxfrancis
Sometimes a cigar is just a cigar.
-- Sigmund Freud
Jul 14 '06 #10

P: n/a
Erik Max Francis wrote:
For enrichment purposes, is there a way to do this sort of thing with
a generator? E.g. something like:

def SentenceGenerator():
words = ['I', 'have', 'been', 'to', 'the', 'fair']
for w in words:
yield w

message = "%s %s %s %s"

print message % SentenceGenerator()

(I ask because the above doesn't work)?

Use tuple(SentenceGenerator()). A generator is just another object, so
using it with the % operator tries to substitute it at one value. (Even
with this fix, though, your message didn't have enough formatters.)
I know that the message didn't have enough formatters, that's why I
asked. (Although I would have assumed that the generator would get
automatically converted to a sequence that was consumable by the
interpolation operator...)

When using a generator via .next(), there's no requirement that it is
used until it is exhausted. E.g.

def NewId():
v = 0
while 1:
yield v
v += 1

NextId = NewId().next

BUTTON_OK = NextId()
BUTTON_CANCEL = NextId()
BUTTON_YAY = NextId()

Hence my question being "something like" rather than "something
equivalent to". Alas, little did I know that the answer I was looking
for was not even up the same path.

-tom!
Jul 14 '06 #11

P: n/a
Tom Plunket wrote:
I know that the message didn't have enough formatters, that's why I
asked. (Although I would have assumed that the generator would get
automatically converted to a sequence that was consumable by the
interpolation operator...)
That's because::

aFormatString % anObject

is a shortcut for::

aFormatString % (anObject,)

so things like::

print "Your birthday is on %s" % date

are allowed. So when the object is an iterator, it's just treated as a
single value in a 1-tuple, rather than iterated over to get its own tuple.

--
Erik Max Francis && ma*@alcyone.com && http://www.alcyone.com/max/
San Jose, CA, USA && 37 20 N 121 53 W && AIM erikmaxfrancis
Sometimes a cigar is just a cigar.
-- Sigmund Freud
Jul 14 '06 #12

This discussion thread is closed

Replies have been disabled for this discussion.