9
\$\begingroup\$

Everyone knows "99 bottles of beer on the wall".

Mat's Mug made a comment about mugs on the wall in The 2nd Monitor and I realized I never wrote a "beer on the wall" program. But that seemed way too easy, so I made it a "n number of x on the y" instead. It also seemed like a good excuse to use templates in Python, which I hadn't done before.

import sys

n = int(sys.argv[1])
x = sys.argv[2]
y = sys.argv[3]

template = '''\
%i %s on the %s
%i %s
Take one down, pass it around
%i %s on the %s
'''

for n in range(n, 0, -1):
    print template % (n, x, y, n, x, n-1, x, y)

Usage:

python script.py 99 "mugs of beer" wall

For obvious reasons there's no way (that I know of) to remove multiples of x if n = 1. Sure, it's not hard to turn mugs of beer into mug of beer, but it's generic. One can input anything and I can't catch all methods of pluralization.

I prefer readability over 'clever code' and my naming is probably not too good.

\$\endgroup\$
  • 1
    \$\begingroup\$ n number of x on the y is mainly there because the title would be too short otherwise. It's actually n x on the y \$\endgroup\$ – Mast Aug 19 '15 at 16:52
  • 4
    \$\begingroup\$ For the record, "mugs on the wall" is referring to the number of rep points by which I'm ahead of Simon André Forsberg. There's been "simons on the wall" for the longest time though. \$\endgroup\$ – Mathieu Guindon Aug 19 '15 at 17:00
13
\$\begingroup\$

Your code is simple and just works. For that reason, you can be happy and there is not much comment to give. However, being very picky just for the sake of learning, one could say various things.

print statements

You are using print without parenthesis. This is the one and only reason why your code wouldn't work on Python 3. Just to be future (well, actually Python 3 is not the future, it is the present) proof, you might as well add the 2 parenthesis.

String formatting

Instead of using the % format, you could use the format method. You'll find more information online. Also using named place holder makes things easier to read and avoid duplicated values.

You'd get something like :

template = '''\
{idx} {x} on the {y} 
{idx} {x}
Take one down, pass it around
{prev} {x} on the {y}
'''

for n in range(n, 0, -1):
    print(template.format(idx=n, prev=n-1, x=x, y=y))

Error handling

You should probably check the number of arguments provided and tell the user if anything is wrong.

Variable names

Even though n is acceptable, x and y do not convey much information, a better name can probably be found.

\$\endgroup\$
8
\$\begingroup\$

I would take @Josay's suggestion one step further, and use more self-descriptive template parameters:

template = '''\
{num} {things} on the {where}
{num} {things}
Take one down, pass it around
{remaining} {things} on the {where}
'''

And command line argument validation can be really easy using argparse:

from argparse import ArgumentParser    

template = '''\
{num} {things} on the {where}
{num} {things}
Take one down, pass it around
{remaining} {things} on the {where}
'''

parser = ArgumentParser(description='N number of THINGS on the WHERE')
parser.add_argument('num', type=int, help='For example 99')
parser.add_argument('things', help='For example "mugs of beer"')
parser.add_argument('where', help='For example "wall"')

args = parser.parse_args()

num = args.num
things = args.things
where = args.where

for n in range(num, 0, -1):
    print(template.format(num=n, remaining=n-1, things=things, where=where))

This will take care of validating the number of arguments, and that num is an integer, for example:

$ python3 beers.py 99 'mugs of beer'
usage: beers.py [-h] num things where
beers.py: error: the following arguments are required: where
$ python3 beers.py 99x 'mugs of beer' wall
usage: beers.py [-h] num things where
beers.py: error: argument num: invalid int value: '99x'

And by passing the script the -h or --help flags you get a nice descriptive usage message:

$ python3 beers.py 99 'mugs of beer' -h
usage: beers.py [-h] num things where

N number of THINGS on the WHERE

positional arguments:
  num         For example 99
  things      For example "mugs of beer"
  where       For example "wall"

optional arguments:
  -h, --help  show this help message and exit
\$\endgroup\$
  • \$\begingroup\$ The link for Python (not Python3) is here, but it seems the usage is the same. I wasn't aware there was a better argument handler than sys, I'm definitely going to implement this in my current and future projects. \$\endgroup\$ – Mast Aug 19 '15 at 21:33
  • \$\begingroup\$ Using sys was not considered a good option since Python 2.3, when optparse was released. optparse used to be the favored solution until 2.7, when it was superseded by argparse \$\endgroup\$ – janos Aug 19 '15 at 21:43
6
\$\begingroup\$

You should use str.format instead, because you can re-use parameters that way.

template = '''\
{0} {1} on the {2}
{0} {1}
Take one down, pass it around
{3} {1} on the {2}
'''

for n in range(n, 0, -1):
    print (template.format(n, x, y, n-1))

I think this is more readable than 6 parameters in a row. I also added brackets around your print because it's neater and forward compatible.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.