第十八章 Arguments
1.Argument-Passing Basics
- Arguments are passed by automatically assigning objects to local variable names.
- Assigning to argument names inside a function does not affect the caller.
- Changing a mutable object argument in a function may impact the caller.
Argument Matching Basics:
- Positionals: matched from left to right
- Keywords: matched by argument name
- Defaults: specify values for optional arguments that aren’t passed
- Varargs collecting: collect arbitrarily many positional or keyword arguments
- Varargs unpacking: pass arbitrarily many positional or keyword arguments
- Keyword-only arguments: arguments that must be passed by name
The Gritty Details:
- In a function call, arguments must appear in this order: any positional arguments
(value); followed by a combination of any keyword arguments (name=value) and
the *iterable form; followed by the **dict form. - In a function header, arguments must appear in this order: any normal arguments
(name); followed by any default arguments (name=value); followed by the *name (or
* in 3.X) form; followed by any name or name=value keyword-only arguments (in
3.X); followed by the **name form.
参数匹配:
- Assign nonkeyword arguments by position
- Assign keyword arguments by matching names.
- Assign extra nonkeyword arguments to *name tuple.
- Assign extra keyword arguments to **name dictionary.
- Assign default values to unassigned arguments in header.
def f(a,b,c):
print(a,b,c)
f(1,2,3)
f(c=3,b=2,a=1)
f(1,c=3,b=2)
f(3,a=1,b=2)
# f(b=3,c=4,1) SyntaxError: non-keyword arg after keyword arg
>>> ================================ RESTART ================================
>>>
1 2 3
1 2 3
1 2 3
Traceback (most recent call last):
File "D:\test.py", line 10, in <module>
f(3,a=1,b=2)
TypeError: f() got multiple values for argument 'a'
Arbitrary Arguments Examples:
Headers: Collecting arguments
>>> def f(*args): print(args) #collects unmatched positional arguments into a tuple
>>> f()
()
>>> f(1)
(1,)
>>> f(1, 2, 3, 4)
(1, 2, 3, 4)
>>> def f(**args): print(args) #works for keyword arguments,collects them into a new dictionary
>>> f()
{}
>>> f(a=1, b=2)
{'a': 1, 'b': 2}
>>> def f(a, *pargs, **kargs): print(a, pargs, kargs)
>>> f(1, 2, 3, x=1, y=2)
1 (2, 3) {'y': 2, 'x': 1}
Calls: Unpacking arguments
注意:one star means positionals, and two applies to keywords.
Python 3.X Keyword-Only Arguments
>>> def kwonly(a, *b, c): #参数c只能用Keyword传参
print(a, b, c)
>>> kwonly(1, 2, c=3)
1 (2,) 3
>>> kwonly(a=1, c=3)
1 () 3
>>> kwonly(1, 2, 3)
TypeError: kwonly() missing 1 required keyword-only argument: 'c'
>>> def kwonly(a, *, b, c): #does not accept a variable-length argument list but still expects all arguments following the * to be passed as keywords
print(a, b, c)
>>> kwonly(1, c=3, b=2)
1 2 3
>>> kwonly(c=3, b=2, a=1)
1 2 3
>>> kwonly(1, 2, 3)
TypeError: kwonly() takes 1 positional argument but 3 were given
>>> kwonly(1)
TypeError: kwonly() missing 2 required keyword-only arguments: 'b' and 'c'
Ordering rules:
keyword-only arguments must be specified after a single star, not two
>>> def kwonly(a, **pargs, b, c):
SyntaxError: invalid syntax
>>> def kwonly(a, **, b, c):
SyntaxError: invalid syntax
>>> def f(a, *b, **d, c=6): print(a, b, c, d) # Keyword-only before **!
SyntaxError: invalid syntax
>>> def f(a, *b, c=6, **d): print(a, b, c, d) # Collect args in header
>>> f(1, 2, 3, x=4, y=5) # Default used
1 (2, 3) 6 {'y': 5, 'x': 4}
>>> f(1, 2, 3, x=4, y=5, c=7) # Override default 注意与下面对比
1 (2, 3) 7 {'y': 5, 'x': 4}
>>> f(1, 2, 3, c=7, x=4, y=5) # Anywhere in keywords
1 (2, 3) 7 {'y': 5, 'x': 4}
>>> def f(a, c=6, *b, **d): print(a, b, c, d) # c is not keyword-only here!
>>> f(1, 2, 3, x=4)
1 (3,) 2 {'x': 4}
function calls: when keyword-only arguments are passed, they must appear before a **args form.
keyword-only argument can be coded either before or after the *args, though, and may be included in **args:
>>> def f(a, *b, c=6, **d): print(a, b, c, d) # KW-only between * and **
>>> f(1, *(2, 3), **dict(x=4, y=5)) # Unpack args at call
1 (2, 3) 6 {'y': 5, 'x': 4}
>>> f(1, *(2, 3), **dict(x=4, y=5), c=7) # Keywords before **args!
SyntaxError: invalid syntax
>>> f(1, *(2, 3), c=7, **dict(x=4, y=5)) # Override default
1 (2, 3) 7 {'y': 5, 'x': 4}
>>> f(1, c=7, *(2, 3), **dict(x=4, y=5)) # After or before *
1 (2, 3) 7 {'y': 5, 'x': 4}
>>> f(1, *(2, 3), **dict(x=4, y=5, c=7)) # Keyword-only in ** 也可在**中
1 (2, 3) 7 {'y': 5, 'x': 4}
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 # See also: lambda
def grtrthan(x, y): return x > y
print(minmax(lessthan, 4, 2, 1, 5, 6, 3)) # Self-test code
print(minmax(grtrthan, 4, 2, 1, 5, 6, 3))
def sumtree(L):
tot = 0
for x in L: # For each item at this level
if not isinstance(x, list):
tot += x # Add numbers directly
else:
tot += sumtree(x) # Recur for sublists
return tot
L = [1, [2, [3, 4], 5], 6, [7, 8]] # Arbitrary nesting
print(sumtree(L)) # Prints 36
# Pathological cases
print(sumtree([1, [2, [3, [4, [5]]]]])) # Prints 15 (right-heavy)
print(sumtree([[[[[1], 2], 3], 4], 5])) # Prints 15 (left-heavy)
# breadth-first by items: add to end
def sumtree(L): # Breadth-first, explicit queue
tot = 0
items = list(L) # Start with copy of top level
while items:
trace(items)
front = items.pop(0) # Fetch/delete front item
if not isinstance(front, list):
tot += front # Add numbers directly
visit(front)
else:
items.extend(front) # <== Append all in nested list
return tot
L = [1, [2, [3, 4], 5], 6, [7, 8]] # Arbitrary nesting
print(sumtree(L)) # Prints 36
# Pathological cases
print(sumtree([1, [2, [3, [4, [5]]]]])) # Prints 15 (right-heavy)
print(sumtree([[[[[1], 2], 3], 4], 5])) # Prints 15 (left-heavy)
print('-'*40)
# depth-first by items: add to front (like recursive calls version)
def sumtree(L): # Depth-first, explicit stack
tot = 0
items = list(L) # Start with copy of top level
while items:
trace(items)
front = items.pop(0) # Fetch/delete front item
if not isinstance(front, list):
tot += front # Add numbers directly
visit(front)
else:
items[:0] = front # <== Prepend all in nested list
return tot
L = [1, [2, [3, 4], 5], 6, [7, 8]] # Arbitrary nesting
print(sumtree(L)) # Prints 36
# Pathological cases
print(sumtree([1, [2, [3, [4, [5]]]]])) # Prints 15 (right-heavy)
print(sumtree([[[[[1], 2], 3], 4], 5])) # Prints 15 (left-heavy)
print('-'*40)
Function Objects: Attributes and Annotations:
annotations work only in def statements, not lambda expressions
def func(a: 'spam', b: (1, 10), c: float) -> int:
return a + b + c
>>> func.__annotations__
{'return': <class 'int'>, 'c': <class 'float'>, 'a': 'spam', 'b': (1, 10)}
>>> def func(a: 'spam' = 4, b: (1, 10) = 5, c: float = 6) -> int: #使用默认值
return a + b + c
>>> def func(a:'spam'=4, b:(1,10)=5, c:float=6)->int: #空格可有可无
return a + b + c
Anonymous Functions: lambda:
lambda Basics:lambda argument1, argument2,... argumentN : expression using arguments
- lambda is an expression, not a statement.
- lambda’s body is a single expression, not a block of statements.
>>> def func(x, y, z): return x + y + z
>>> func(2, 3, 4)
9
>>> f = lambda x, y, z: x + y + z # f is assigned the function object the lambda expression creates
>>> f(2, 3, 4)
9
lambda expressions introduce a local scope much like a nested def, which automatically
sees names in enclosing functions, the module, and the built-in scope (via the
LEGB rule,
L = [lambda x: x ** 2, # Inline function definition
lambda x: x ** 3,
lambda x: x ** 4] # A list of three callable functions
for f in L:
print(f(2)) # Prints 4, 8, 16
print(L[0](3)) # Prints 9
lambda内使用循环时,方法一使用map,方法二 list comprehension expressions
>>> import sys
>>> showall = lambda x: list(map(sys.stdout.write, x)) # 3.X: must use list
>>> t = showall(['spam\n', 'toast\n', 'eggs\n']) # 3.X: can use print
spam
toast
eggs
>>> showall = lambda x: [sys.stdout.write(line) for line in x]
>>> t = showall(('bright\n', 'side\n', 'of\n', 'life\n'))
bright
side
of
life
>>>
import sys
from tkinter import Button, mainloop # Tkinter in 2.X
x = Button(
text='Press me',
command=(lambda:sys.stdout.write('Spam\n'))) # 3.X: print()
x.pack()
mainloop() # This may be optional in console mode
Mapping Functions over Iterables: map
>>> counters = [1, 2, 3, 4]
>>> def inc(x): return x + 10 # Function to be run
>>> list(map(inc, counters)) # Collect results
[11, 12, 13, 14]
>>> list(map((lambda x: x + 3), counters)) # Function expression
[4, 5, 6, 7]
>>> pow(3, 4) # 3**4
81
>>> list(map(pow, [1, 2, 3], [2, 3, 4])) # 1**2, 2**3, 3**4
[1, 8, 81]
Selecting Items in Iterables: filter
>>> list(filter((lambda x: x > 0), range(−5, 5))) # An iterable in 3.X
[1, 2, 3, 4]
Combining Items in Iterables: reduce
>>> from functools import reduce # Import in 3.X, not in 2.X
>>> reduce((lambda x, y: x + y), [1, 2, 3, 4])
10
>>> reduce((lambda x, y: x * y), [1, 2, 3, 4])
24