Learning Python - Mark Lutz [324]
print('I am:', __name__)
def minmax(test, *args):
res = args[0]
for arg in args[1:]:
if test(arg, res):
res = arg
return res
def lessthan(x, y): return x < y
def grtrthan(x, y): return x > y
if __name__ == '__main__':
print(minmax(lessthan, 4, 2, 1, 5, 6, 3)) # Self-test code
print(minmax(grtrthan, 4, 2, 1, 5, 6, 3))
We’re also printing the value of __name__ at the top here to trace its value. Python creates and assigns this usage-mode variable as soon as it starts loading a file. When we run this file as a top-level script, its name is set to __main__, so its self-test code kicks in automatically:
% python min.py
I am: __main__
1
6
But, if we import the file, its name is not __main__, so we must explicitly call the function to make it run:
>>> import min
I am: min
>>> min.minmax(min.lessthan, 's', 'p', 'a', 'm')
'a'
Again, regardless of whether this is used for testing, the net effect is that we get to use our code in two different roles—as a library module of tools, or as an executable program.
Using Command-Line Arguments with __name__
Here’s a more substantial module example that demonstrates another way that the __name__ trick is commonly employed. The following module, formats.py, defines string formatting utilities for importers, but also checks its name to see if it is being run as a top-level script; if so, it tests and uses arguments listed on the system command line to run a canned or passed-in test. In Python, the sys.argv list contains command-line arguments—it is a list of strings reflecting words typed on the command line, where the first item is always the name of the script being run:
"""
Various specialized string display formatting utilities.
Test me with canned self-test or command-line arguments.
"""
def commas(N):
"""
format positive integer-like N for display with
commas between digit groupings: xxx,yyy,zzz
"""
digits = str(N)
assert(digits.isdigit())
result = ''
while digits:
digits, last3 = digits[:-3], digits[-3:]
result = (last3 + ',' + result) if result else last3
return result
def money(N, width=0):
"""
format number N for display with commas, 2 decimal digits,
leading $ and sign, and optional padding: $ -xxx,yyy.zz
"""
sign = '-' if N < 0 else ''
N = abs(N)
whole = commas(int(N))
fract = ('%.2f' % N)[-2:]
format = '%s%s.%s' % (sign, whole, fract)
return '$%*s' % (width, format)
if __name__ == '__main__':
def selftest():
tests = 0, 1 # fails: −1, 1.23
tests += 12, 123, 1234, 12345, 123456, 1234567
tests += 2 ** 32, 2 ** 100
for test in tests:
print(commas(test))
print('')
tests = 0, 1, −1, 1.23, 1., 1.2, 3.14159
tests += 12.34, 12.344, 12.345, 12.346
tests += 2 ** 32, (2 ** 32 + .2345)
tests += 1.2345, 1.2, 0.2345
tests += −1.2345, −1.2, −0.2345
tests += −(2 ** 32), −(2**32 + .2345)
tests += (2 ** 100), −(2 ** 100)
for test in tests:
print('%s [%s]' % (money(test, 17), test))
import sys
if len(sys.argv) == 1:
selftest()
else:
print(money(float(sys.argv[1]), int(sys.argv[2])))
This file works the same in Python 2.6 and 3.0. When run directly, it tests itself as before, but it uses options on the command line to control the test behavior. Run this file directly with no command-line arguments on your own to see what its self-test code prints. To test specific strings, pass them in on the command line along with a minimum field width:
C:\misc> python formats.py 999999999 0
$999,999,999.00
C:\misc> python formats.py −999999999 0
$-999,999,999.00
C:\misc> python formats.py 123456789012345 0
$123,456,789,012,345.00
C:\misc> python formats.py −123456789012345 25
$ −123,456,789,012,345.00
C:\misc> python formats.py 123.456 0
$123.46
C:\misc> python formats.py −123.454 0
$-123.45
C:\misc> python formats.py
...canned tests: try this yourself...
As before, because this code is instrumented for dual-mode usage, we can also import its tools normally in other contexts as library components:
>>> from formats