Learning Python - Mark Lutz [591]
return good + bad + ugly
print(adder())
print(adder(5))
print(adder(5, 6))
print(adder(5, 6, 7))
print(adder(ugly=7, good=6, bad=5))
% python mod.py
6
10
14
18
18
# Second part solutions
def adder1(*args): # Sum any number of positional args
tot = args[0]
for arg in args[1:]:
tot += arg
return tot
def adder2(**args): # Sum any number of keyword args
argskeys = list(args.keys()) # list needed in 3.0!
tot = args[argskeys[0]]
for key in argskeys[1:]:
tot += args[key]
return tot
def adder3(**args): # Same, but convert to list of values
args = list(args.values()) # list needed to index in 3.0!
tot = args[0]
for arg in args[1:]:
tot += arg
return tot
def adder4(**args): # Same, but reuse positional version
return adder1(*args.values())
print(adder1(1, 2, 3), adder1('aa', 'bb', 'cc'))
print(adder2(a=1, b=2, c=3), adder2(a='aa', b='bb', c='cc'))
print(adder3(a=1, b=2, c=3), adder3(a='aa', b='bb', c='cc'))
print(adder4(a=1, b=2, c=3), adder4(a='aa', b='bb', c='cc'))
(and 6.) Here are my solutions to exercises 5 and 6 (file dicts.py). These are just coding exercises, though, because Python 1.5 added the dictionary methods D.copy() and D1.update(D2) to handle things like copying and adding (merging) dictionaries. (See Python’s library manual or O’Reilly’s Python Pocket Reference for more details.) X[:] doesn’t work for dictionaries, as they’re not sequences (see Chapter 8 for details). Also, remember that if you assign (e = d) rather than copying, you generate a reference to a shared dictionary object; changing d changes e, too:def copyDict(old):
new = {}
for key in old.keys():
new[key] = old[key]
return new
def addDict(d1, d2):
new = {}
for key in d1.keys():
new[key] = d1[key]
for key in d2.keys():
new[key] = d2[key]
return new
% python
>>> from dicts import *
>>> d = {1: 1, 2: 2}
>>> e = copyDict(d)
>>> d[2] = '?'
>>> d
{1: 1, 2: '?'}
>>> e
{1: 1, 2: 2}
>>> x = {1: 1}
>>> y = {2: 2}
>>> z = addDict(x, y)
>>> z
{1: 1, 2: 2}
See #5.
More argument-matching examples. Here is the sort of interaction you should get, along with comments that explain the matching that goes on:def f1(a, b): print(a, b) # Normal args
def f2(a, *b): print(a, b) # Positional varargs
def f3(a, **b): print(a, b) # Keyword varargs
def f4(a, *b, **c): print(a, b, c) # Mixed modes
def f5(a, b=2, c=3): print(a, b, c) # Defaults
def f6(a, b=2, *c): print(a, b, c) # Defaults and positional varargs
% python
>>> f1(1, 2) # Matched by position (order matters)
1 2
>>> f1(b=2, a=1) # Matched by name (order doesn't matter)
1 2
>>> f2(1, 2, 3) # Extra positionals collected in a tuple
1 (2, 3)
>>> f3(1, x=2, y=3) # Extra keywords collected in a dictionary
1 {'x': 2, 'y': 3}
>>> f4(1, 2, 3, x=2, y=3) # Extra of both kinds
1 (2, 3) {'x': 2, 'y': 3}
>>> f5(1) # Both defaults kick in
1 2 3
>>> f5(1, 4) # Only one default used
1 4 3
>>> f6(1) # One argument: matches "a"
1 2 ()
>>> f6(1, 3, 4) # Extra positional collected
1 3 (4,)
Primes revisited. Here is the primes example, wrapped up in a function and a module (file primes.py) so it can be run multiple times. I added an if test to trap negatives, 0, and 1. I also changed / to // in this edition to make this solution immune to the Python 3.0 / true division changes we studied in Chapter 5, and to enable it to support floating-point numbers (uncomment the from statement and change // to / to see the differences in 2.6):#from __future__ import division
def prime(y):
if y <= 1: # For some y > 1
print(y, 'not prime')
else:
x = y // 2 # 3.0 / fails
while x > 1:
if y % x == 0: # No remainder?
print(y, 'has factor', x)
break # Skip else
x -= 1
else:
print(y, 'is prime')