图7-1-1所示的函数基本格式中,圆括号里面的参数是可选项。如果为空,即没有参数,如前面使用过的函数 laoqi()
那样。如果不为空,如7.1.2节中定义的 fibo_loop()
函数那样,在调用它的时候就要“向函数传对象”——注意带有引号的说法。在 Python 中,“向函数传对象”或者“向函数传值”、“向函数传参数”,这些说法的含义都是一样的,也都是简化了的俗语——不严格,但形象直接。
1. 位置参数
根据位置“向函数传值”是最常见的一种方式,并且也非常直接、简单——简称位置参数。例如:
>>> def foo(a, b, c):
... print(f"a = {a}, b = {b}, c = {c}")
...
>>> foo(1, "python", [9, 8, 7])
a = 1, b = python, c = [9, 8, 7]
函数 foo()
有多个参数,每个参数之间用逗号(英文状态)隔开。在 foo(1, "python", [9, 8, 7])
的圆括号内,有多个成员对象,每个对象依次与 foo(a, b, c)
中的 a, b, c
对应,即:
Parameter | Argument | |
---|---|---|
a | ← | 1 |
b | ← | 'python' |
c | ← | [9, 8, 7] |
Parameter(参数)列中的是函数 foo()
的“参数”,Argument(论据)列中的是“对象”(或者称“实例”),通过位置对应关系,将 Parameter 与 Argument 建立映射关系。换个角度,函数中的 Parameter(参数)就是变量,所谓“向函数传值”就是将这些变量与对象建立引用关系——还是变量与对象的关系。诸如下面的说法都是在具体语境中的一种形象、简便且不很严谨的表述:
1
传给函数 foo()
”;foo()
传入列表”;foo()
传参数”;类似的表述在本书中还会使用,但读者要了解其本质:建立参数(变量)与对象的引用关系。
>>> lst = [1, 2, 3]
>>> def bar(a):
... print(f"a is {a}, id is {id(a)}")
... a.append(99) # (7)
... print(f"a is {a}, id is {id(a)}")
...
>>> bar(lst)
a is [1, 2, 3, 99], id is 140374963517312
a is [1, 2, 3, 99, 99], id is 140374963517312
>>> id(lst)
140374963517312
>>> lst
[1, 2, 3, 99, 99]
以上操作示例中,充分体现了“变量与对象之间的引用关系”:
bar()
外面创建了一个列表对象 lst
——这也是一种简化说法,本质是变量 lst
引用列表对象。bar()
里面对参数 a
追加一个对象(如注释(7)所示)——实质上是调用参数或变量 a
所引用的对象的 append()
方法。lst
传给函数 bar(lst)
的本质就是参数 a
与变量 lst
都引用了列表对象 [1, 2, 3]
(如图7-1-2所示),因此,当函数内部注释(7)向该列表追加成员之后,函数外面的 lst
引用的列表内的成员也随之变化——内外列表本质是同一个对象。图7-1-2 参数与对象关系
显然,“位置参数”强调位置对应关系,例如 foo(1, [9, 8, 7], 'python')
与前述示例的结果必然不同。
>>> foo(1, [9, 8, 7], 'python')
a = 1, b = [9, 8, 7], c = python
不仅强调位置的顺序,对于类似于 foo()
那种形式的参数而言,对应的对象一个都不能少。
>>> foo(1, [9, 8, 7])
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: foo() missing 1 required positional argument: 'c'
读者如果阅读更多关于编程语言函数资料,常会看到形参(Formal Parameter)和实参(Actual Parameter)这两个术语。以函数 foo()
为例,如图7-1-3所示,当调用它时,圆括号内的对象就是函数的实参,即 Arguments(论据、实例);定义它时,圆括号内的就是形参,即 Parameters(参数)。
图7-1-3 形参与实参
2. 关键词参数
如果像下面这样调用函数 foo()
:
>>> foo(a=1, b='python', c=[9,8,7])
a = 1, b = python, c = [9, 8, 7]
>>> foo(a=1, c=[9,8,7], b='python')
a = 1, b = python, c = [9, 8, 7]
将形参与实参绑定,则不论次序如何,对象的引用关系不受影响。像这样“向函数传参数”的方式简称为关键词参数。注意,在关键词参数中,不能使用函数中不存在的形参,比如:
>>> foo(a=1, c=[9,8,7], d='book')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: foo() got an unexpected keyword argument 'd'
并且数量也不能少。
>>> foo(a=1, c=[9,8,7])
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: foo() missing 1 required positional argument: 'b'
此外,关键词参数和位置参数可以混用。
>>> foo(1, 'python', c=[9,8,7])
a = 1, b = python, c = [9, 8, 7]
特别要注意,在既有位置参数,又有关键词参数时,务必要将位置参数放在关键词参数前面,否则就会报错。
>>> foo(1, b='python', [9,8,7])
File "<stdin>", line 1
foo(1, b='python', [9,8,7])
^
SyntaxError: positional argument follows keyword argument
关键词参数的本质与前述一样,依然是建立了“参数(变量)与对象的引用关系”。
3. 设置默认参数
有的情况下,在定义函数的同时为形参设置一个默认的值,例如:
>>> def bar(book, age, name='laoqi'):
... print(f"{name}, a {age}-year-old coder, his book is {book}.")
...
>>> bar('ML', 30)
laoqi, a 30-year-old coder, his book is ML.
>>> bar('ML', 30, 'zhangsan')
zhangsan, a 30-year-old coder, his book is ML.
为 bar()
的形参 name
设置了默认值——注意,要放在未赋值的参数后面。如果以 bar('ML', 30)
的方式调用此函数,则使用此默认值。也可以用 bar('ML', 30, 'zhangsan')
修改 name
的默认值。