当你需要编写大型程序时,你可能想要写一段代码供其它地方调用,或者将程序中的相同逻辑抽出来,这时你就需要用到函数。但函数不仅能提供这些功能,通过使用函数还能帮助我们更容易的理解代码。
定义函数
下面的代码定义了一个函数fib,用于计算Fibonacci数列:
>>> def fib(n):
"""Print a Fibonacci series up to n."""
a, b = 0, 1
while a < n:
print(a, end=' ')
a, b = b, a+b
print()
>>> fib(200)
0 1 1 2 3 5 8 13 21 34 55 89 144
首先是关键词def,后面跟函数名和参数列表。从下一行开始就是函数的主体,必须缩进。
函数的第一行可以是描述性的字符串,可选,如果有,则会作为函数的一部分进行存储,成为文档字符串。有工具可以使用文档字符串自动生成在线或者打印的文档,也可以通过下面的方式访问:
>>> fib.__doc__
'Print a Fibonacci series up to n.'
或者使用help函数:
>>> help(fib)
Help on function fib in module __main__:
fib(n)
Print a Fibonacci series up to n.
函数可以使用return返回一个值,例如,为上面的fib函数返回一个结果序列:
>>> def fib2(n):
"""Return a list containing the Fibonacci series up to n."""
result = []
a, b = 0, 1
while a < n:
result.append(a)
a, b = b, a+b
return result
>>> f100 = fib2(100)
>>> f100
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]
如果函数没有return语句,则返回None,如:
>>> f100 = fib(100)
0 1 1 2 3 5 8 13 21 34 55 89
>>> print(f100)
None
下面介绍更多python中函数的更多特性。
为参数指定默认值
为函数参数指定默认值非常有用,这样在调用该函数时可以指定更少的参数,例如:
>>> def ask_ok(prompt, retries=4, complaint='Yes or no, please!'):
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 OSError('uncooperative user')
print(complaint)
该函数你可以通过下面的方式调用:
1)仅指定强制参数:
>>> ask_ok('Do you really want to quit?')
Do you really want to quit?y
True
2)给可选参数中的一个:
>>> ask_ok('OK to overwrite the file?', 2)
OK to overwrite the file?n
False
3)给出所有参数:
>>> ask_ok('OK to overwrite the file?', 2, 'Come on, only yes or no!')
OK to overwrite the file?n
False
需要注意的是,函数的默认值在定义的一刻就确定了,例如:
>>> i = 5
>>> def f(arg=i):
print(arg)
>>> i = 6
>>> f()
5
由于定义f的时候i的值为5,所以调用f()总是打印5。
警告:默认值只会赋值一次,但当默认值是一个可变对象时(例如list、字典、或者可变类的实例),将可能存在问题,看下面的例子,将传入的值累计在了一起:
>>> def f(a, L=[]):
L.append(a)
return L
>>> print(f(1))
[1]
>>> print(f(2))
[1, 2]
>>> print(f(3))
[1, 2, 3]
如果你不想在的多个调用间共享默认值,你可以这样做:
>>> def f(a, L=None):
if 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, "!")
parrot指定了一个强制参数和3个可选参数。parrot可以使用下面的形式调用:
parrot(1000)
parrot(voltage=1000)
parrot(voltage=1000000, action='VOOOOOM')
parrot(action='VOOOOOM', voltage=1000000)
parrot('a million', 'bereft of life', 'jump')
parrot('a thousand', state='pushing up the daisies')
注意下面的调用方式是无效的:
parrot()
parrot(voltage=5.0, 'dead')
parrot(110, voltage=220)
parrot(actor='John Cleese')
可变长度参数
函数支持可变长度参数,传入的参数将被打包为一个元组,在可变参数之前,0个或者多个参数可以提供。
>>> def write_multiple_items(file, separator, *args):
file.write(separator.join(args))
*args表示可以输入任意个参数。通常,可变参数都会放到参数列表的最后,因为他们将包括所有后面的输入参数。任何可变参数后面的参数都必须以关键字参数的形式使用。下面看一个具体的例子:
>>> def concat(*args, sep="/"):
return sep.join(args)
>>> concat("earth", "mars", "venus")
'earth/mars/venus'
>>> concat("earth", "mars", "venus", sep=".")
'earth.mars.venus'
除了'*',python还支持'**'。'**'表示传入的参数为字典,看下面的例子:
>>> def print_params(**params):
print(params)
>>> print_params(x=1,y=2,z=3)
{'z': 3, 'x': 1, 'y': 2}
这样,打印出params为一个字典。下面看一个综合的例子:
>>> def cheeseshop(kind, *arguments, **keywords):
print("-- Do you have any", kind, "?")
print("-- I'm sorry, we're all out of", kind)
for arg in arguments:
print(arg)
print("-" * 40)
keys = sorted(keywords.keys())
for kw in keys:
print(kw, ":", keywords[kw])
>>> cheeseshop("Limburger", "It's very runny, sir.",
"It's really very, VERY runny, sir.",
shopkeeper="Michael Palin",
client="John Cleese",
sketch="Cheese Shop Sketch")
-- Do you have any Limburger ?
-- I'm sorry, we're all out of Limburger
It's very runny, sir.
It's really very, VERY runny, sir.
----------------------------------------
client : John Cleese
shopkeeper : Michael Palin
sketch : Cheese Shop Sketch
解包list和元组
前面已经使用'*'和'**'将传入参数打包为一个元组和字典,但如果参数已经为一个list或者元组,怎么将其作为参数传入函数呢?可以使用'*'和'**'来将其解包。看下面的例子:
>>> args = [3, 6]
>>> list(range(*args))
[3, 4, 5]
同样的方式,字典可以作为关键字参数使用,通过使用'**'操作符:
>>> def parrot(voltage, state='a stiff', action='voom'):
print("-- This parrot wouldn't", action, end=' ')
print("if you put", voltage, "volts through it.", end=' ')
print("E's", state, "!")
>>> d = {"voltage": "four million", "state": "bleedin' demised", "action": "VOOM"}
>>> parrot(**d)
-- This parrot wouldn't VOOM if you put four million volts through it. E's bleedin' demised !
Lambda表达式
使用lambda关键字可以创建小的匿名的函数,例如下面的函数返回两个参数的和:
lambda a, b: a+b
lambda函数在语法上被限制为单表达式,语义上,他们仅是通常的函数定义的简化语法。像嵌套函数定义,lambda函数引用包含范围内的变量。
>>> def make_incrementor(n):
return lambda x: x + n
>>> f = make_incrementor(42)
>>> f(0)
42
>>> f(1)
43
上面是使用Lambda表达式返回了一个函数,下面是另一个例子,传递一个小的函数作为参数:
>>> pairs = [(1, 'one'), (2, 'two'), (3, 'three'), (4, 'four')]
>>> pairs.sort(key=lambda pair: pair[1])
>>> pairs
[(4, 'four'), (1, 'one'), (3, 'three'), (2, 'two')]
函数注释
函数注释是可选的,包括关于用户定义函数的任意的信息。python本身和标准库都不使用函数注释,这里展示一下函数注释的语法。
注释被存储在函数的__annotations__属性中,参数注释通过参数后面加一个冒号定义,被一个表达式跟随;返回注释通过语法->定义。下面是一个例子:
>>> def f(ham: 42, eggs: int = 'spam') -> "Nothing to see here":
print("Annotations:", f.__annotations__)
print("Arguments:", ham, eggs)
>>> f('wonderful')
Annotations: {'return': 'Nothing to see here', 'ham': 42, 'eggs': <class 'int'>}
Arguments: wonderful spam
作用域
python中变量被放在一个字典中,通过“名称->值”的形式存储。内建的vars函数可以返回这个字典:
>>> x = 1
>>> scope = vars()
>>> scope['x']
1
这种字典就叫做命名空间或者作用域。每个函数调用都会创建一个新的作用域,函数内的变量被称为局部变量,参数类似于局部变量。
python中提供了方法在函数中访问全局变量,可以使用globals函数获取全局变量值,该函数的近亲是vars,例如:
>>> def combine(parameter):
print(parameter + globals()['parameter'])
>>> parameter = 'berry'
>>> combine('Shrub')
Shrubberry
也可以使用绑定全局变量的方法:
>>> x = 1
>>> def change_global():
global x
x = x + 1
>>> change_global()
>>> x
2
但这样容易导致混乱,建议尽量不要这样做。