1.5 函数与模块
定义函数
我们可以创建一个加总的函数 sumab(),关键字 def 引入一个函数定义,它必须后跟函数名称和带括号的形式参数列表。构成函数体的语句从下一行开始,并且必须缩进。
chap1-5-1-func.py
def sumab(a,b):
return a+b
print(sumab(10,12))
print(sumab)
sumx = sumab
print(sumx(20,22))
print(sumx)
---------------------------------------
22
<function sumab at 0x000001F5AC16F040>
42
<function sumab at 0x000001F5AC16F040>
函数的执行会引入一个用于函数局部变量的新符号表。 更确切地说,函数中所有的变量赋值都将存储在局部符号表中;而变量引用会首先在局部符号表中查找,然后是外层函数的局部符号表,再然后是全局符号表,最后是内置名称的符号表。 因此,全局变量和外层函数的变量不能在函数内部直接赋值(除非是在 global 语句中定义的全局变量,或者是在 nonlocal 语句中定义的外层函数的变量)。在函数被调用时,实际参数(实参)会被引入被调用函数的本地符号表中;因此,实参是通过按值调用 (call by reference) 传递的(其中值始终是对象引用而不是对象的值)。当一个函数调用另外一个函数时,将会为该调用创建一个新的本地符号表。
函数定义会把函数名引入当前的符号表中。函数名称的值具有解释器将其识别为用户定义函数的类型。这个值可以分配给另一个名称,该名称也可以作为一个函数使用。这用作一般的重命名机制。
return 语句会从函数内部返回一个值。 不带表达式参数的 return 会返回 None。 函数执行完毕退出也会返回 None。
参数默认值
给函数定义有可变数目的参数也是可行的,最有用的形式是对一个或多个参数指定一个默认值。这样创建的函数,可以用比定义时允许的更少的参数调用,比如
chap1-5-2-varparameters.py
def ask_ok(prompt, retries=4, reminder='Please try again!'):
while True:
ok = input(prompt)
if ok in ('y', 'ye', 'yes'):
return True
if ok in ('n', 'no', 'nop', 'nope'):
return False
retries = retries - 1
if retries < 0:
raise ValueError('invalid user response')
print(reminder)
# 只给出必需的参数:
ask_ok('Do you really want to quit?')
# 给出一个可选的参数:
ask_ok('OK to overwrite the file?', 2)
# 给出所有的参数:
ask_ok('OK to overwrite the file?', 2, 'Come on, only yes or no!')
这个示例还介绍了 in 关键字。它可以测试一个序列是否包含某个值。
默认值是在 定义过程 中在函数定义处计算的,所以下面的范例为 5 。
i = 5
def f(arg=i):
print(arg)
i = 6
f()
--------------------------
# 输出结果如下:
5
默认值只会执行一次。这条规则在默认值为可变对象(列表、字典以及大多数类实例)时很重要。比如,下面的函数会存储在后续调用中传递给它的参数:
def f(a, L=[]):
L.append(a)
return L
print(f(1))
print(f(2))
print(f(3))
--------------------------
# 输出结果如下:
[1]
[1, 2]
[1, 2, 3]
如果你不想要在后续调用之间共享默认值,你可以这样写这个函数:
def f(a, L=None):
if L is None:
print('L is none')
L = []
L.append(a)
return L
关键字参数
可以使用形如 kwarg=value 的关键字参数来调用函数。例如下面的函数:
def parrot(voltage, state='a stiff', action='voom', type='Norwegian Blue'):
print("-- This parrot wouldn't", action, end=' ')
print("if you put", voltage, "volts through it.")
print("-- Lovely plumage, the", type)
print("-- It's", state, "!")
接受一个必需的参数(voltage)和三个可选的参数(state, action,和 type)。这个函数可以通过下面的任何一种方式调用:
parrot(1000) # 1 位置参数
parrot(voltage=1000) # 1 关键字参数
parrot(voltage=1000000, action='VOOOOOM') # 2 关键字参数
parrot(action='VOOOOOM', voltage=1000000) # 2 关键字参数
parrot('a million', 'bereft of life', 'jump') # 3 位置参数
parrot('a thousand', state='pushing up the daisies') # 1 位置参数, 1 关键字参数
但下面的函数调用都是无效的:
parrot() # 缺乏必需参数
parrot(voltage=5.0, 'dead') # non-keyword argument after a keyword argument
parrot(110, voltage=220) # 相同参数重复给值
parrot(actor='John Cleese') # 不认识的关键字参数
在函数调用中,关键字参数必须跟随在位置参数的后面。传递的所有关键字参数必须与函数接受的其中一个参数匹配,它们的顺序并不重要。这也包括非可选参数,(比如 parrot(voltage=1000) 也是有效的)。不能对同一个参数多次赋值。
*args 参数可以接收一个包含除了已有形参列表以外的位置参数的元组的形参,这些可变参数将在形式参数列表的末尾,因为它们收集传递给函数的所有剩余输入参数。出现在 *args 参数之后的任何形式参数都是 ‘仅关键字参数’,也就是说它们只能作为关键字参数而不能是位置参数。:
def concat(*args, sep=''):
if (sep == ''):
return list(args)
else:
return sep.join(args)
print(concat("earth", "mars", "venus"))
print(concat("earth", "mars", "venus", sep="|"))
------------------------------------------
['earth', 'mars', 'venus']
earth|mars|venus