本系列文章对应 Python 3 版本

函数以关键字def定义,其后紧跟函数名,由一对小括号闭合的形参,最后以冒号结束定义行,定义行下面的是函数体,它是一个语句块。

示例

1
2
3
4
5
6
7
8
9
#!/usr/bin/python
# Filename: function1.py
def sayHello():
print('Hello World!') # 语句块也就是函数体
# 结束函数定义
sayHello() # 调用函数
sayHello() # 再次调用

输出

1
2
3
$ python function1.py
Hello World!
Hello World!

形参

形参在函数定义中的小括号内指定,并以逗号分隔。当我们调用函数时以同样的方式提供形参值。

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#!/usr/bin/python
# Filename: func_param.py
def printMax(a, b):
if a > b:
print(a, 'is maximum')
elif a == b:
print(a, 'is equal to', b)
else:
print(b, 'is maximum')
printMax(3, 4) # 直接提供字面值
x = 5
y = 7
printMax(x, y) # 以变量作为实参

输出

1
2
3
$ python func_param.py
4 is maximum
7 is maximum

局部变量

在函数内声明的变量与在函数外的同名变量没有任何关系,即变量名对于函数是局部的。

这被称作变量的作用域,变量的作用域开始于它们所在块中定义它们的定义点处。

示例

1
2
3
4
5
6
7
8
9
10
11
12
#!/usr/bin/python
# Filename: func_local.py
x = 50
def func(x):
print('x is', x)
x = 2
print('Changed local x to', x)
func(x)
print('x is still', x)

输出

1
2
3
4
$ python func_local.py
x is 50
Changed local x to 2
x is still 50

全局变量

如果你想要为一个定义在函数外的变量赋值,那么你就得告诉Python这个变量名不是局部的,而是 全局 的。我们使用global语句完成这一功能。没有global语句,是不可能为定义在函数外的变量赋值的。

你可以使用定义在函数外的变量的值(假设在函数内没有同名的变量)。然而,我并不鼓励你这样做,并且你应该尽量避免这样做,因为这使得程序的读者会不清楚这个变量是在哪里定义的。使用global语句可以清楚地表明变量是在外面的块定义的。

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
#!/usr/bin/python
# Filename: func_global.py
x = 50
def func():
global x # 用global语句声明一个全局变量x
print('x is', x)
x = 2
print('Changed global x to', x)
func()
print('Value of x is', x)

输出

1
2
3
4
$ python func_global.py
x is 50
Changed global x to 2
Value of x is 2

非局部变量

我们已经看到如何存取局部和全局变量。还有另一类叫做 非局部 的作用域,它介于局部和全局之间。

当在函数内定义函数时你会注意到非局部作用域。

因为python中的一切只是可执行代码,所以你可以在任何地方定义函数。让我们看一个例子:

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
#!/usr/bin/python
# Filename: func_nonlocal.py
def func_outer():
x = 2
print('x is', x)
def func_inner():
nonlocal x
x = 5
func_inner()
print('Changed local x to', x)
func_outer()

输出

1
2
3
$ python func_nonlocal.py
x is 2
Changed local x to 5

说明

当我们处于func_inner函数中时,func_outer第一行定义的变量x既不是局部也不是全局变量。

这时通过nonlocal x我们声明这个x是非局部的,所以我们才能够存取它。

试着将nonlocal x改为global x观察这两种用法有什么不同。

默认实参值

对于一些函数,你可能希望它们的形参是可选的,并当用户没有为这些形参提供值的时候给它们一个默认值。

这需要借助默认实参值。默认实参值在函数定义时通过为形参名赋一个默认值实现。

默认实参值应该是一个常量,更确切的应该是一个不可变类型

示例

1
2
3
4
5
6
7
8
#!/usr/bin/python
# Filename: func_default.py
def say(message, times = 1):
print(message * times)
say('Hello')
say('World', 5)

输出

1
2
3
$ python func_default.py
hello
worldworldworldworldworld

重点

只有在实参列表靠后的参数才能拥有默认实参值 ,即你不能先声明带有默认实参值的形参再声明不带有默认实参值的参数。

这是因为实参值是根据形参的位置赋给形参的,例如:def func(a, b = 5)合法,但def func(a = 5, b)就非法了。

关键实参

如果你有一些函数拥有许多参数,但你只想使用其中的几个,这时你可以通过形参名为其赋值。

这被称做关键实参——使用形参名(关键字)为函数指定实参而不是我们一直使用的通过位置指定实参。

这样做有两个优点,首先函数用起来更简单,因为我们不用操心实参的顺序了。

其次,可以只为我们感兴趣的形参赋值,如果其它参数带有默认实参值的话。

示例

1
2
3
4
5
6
7
8
9
#!/usr/bin/python
# Filename: func_key.py
def func(a, b=5, c=10):
print('a is', a, 'and b is', b, 'and c is', c)
func(3, 7)
func(25, c=24)
func(c=50, a=100)

输出

1
2
3
4
$ python func_key.py
a is 3 and b is 7 and c is 10
a is 25 and b is 5 and c is 24
a is 100 and b is 5 and c is 50

可变参数(VarArgs)

有时你可能希望编写一个可以接受任意多个形参的函数,使用星号可以帮你做到。

示例

1
2
3
4
5
6
7
8
9
10
11
12
#!/usr/bin/python
# Filename: total.py
def total(initial=5, *numbers, **keywords):
count = initial
for number in numbers:
count += number
for key in keywords:
count += keywords[key]
return count
print(total(10, 1, 2, 3, vegetables=50, fruits=100))

输出

1
2
$ python total.py
166

说明

星号-列表:当我们以 星号 声明一个形参比如*param,那么这个参数点之后的所有实参会被收集成一个列表

双星号-关键字实参字典:与之类似如果我们以 双星号 声明一个形参,它会被收集成一个关键字实参字典

只能以关键字赋值的形参##

如果我们希望某些关键字形参只能通过关键字实参得到而不是按照实参的位置得到,可以将其声明在星号形参后面。称为Keyword-only实参:

示例

1
2
3
4
5
6
7
8
9
10
11
12
#!/usr/bin/python
# Filename: keyword_only.py
def total(initial=5, *numbers, vegetables):
count = initial
for number in numbers:
count += number
count += vegetables
return count
print(total(10, 1, 2, 3, vegetables=50))
print(total(10, 1, 2, 3)) # 引发错误,因为我们没有为vegetables提供默认实参值

输出

1
2
3
4
5
6
$ python keyword_only.py
66
Traceback (most recent call last):
File "test.py", line 12, in <module>
print(total(10, 1, 2, 3))
TypeError: total() needs keyword-only argument vegetables

说明

在星号形参后面声明的形参导致它成为keyword-only实参。

如果没有为这些实参提供一个默认值,那么必须在调用函数时以关键字实参为其赋值,否则将引发错误。

如果你只需要keyword-only实参但不需要星号实参,那么可以简单的省略星号实参的实参名。

例如def total(initial=5, *, vegetables)

return语句

return用于从函数返回,即跳出函数。也可以利用return语句从函数返回一个值。

注意一个不带有返回值的return语句相当于返回return None

None是python的一个特殊类型,代表空。例如如果一个变量的值为None则代表它不存在值。每个函数的末尾都隐含的包含一个return None语句除非你编写了自己的return语句。

示例

1
2
3
4
5
6
7
8
9
#!/usr/bin/python
# Filename: func_return.py
def maximum(x, y):
if x > y:
return x
else:
return y
print(maximum(2, 3))

输出

1
2
$ python func_return.py
3

文档字符串

文档字符串(doc string)是一个你应该利用的重要的工具,因为它帮助你更好的注释程序使得程序更易于理解。

更神奇的是你甚至可以在程序运行时取得文档字符串!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#!/usr/bin/python
# Filename: func_doc.py
def printMax(x, y):
'''Prints the maximum of two numbers.
The two values must be integers.'''
x = int(x) # convert to integers, if possible
y = int(y)
if x > y:
print(x, 'is maximum')
else:
print(y, 'is maximum')
printMax(3, 5)
print(printMax.__doc__)

输出

1
2
3
4
$ python func_doc.py
5 is maximum
Prints the maximum of two numbers.
The two values must be integers.

说明

一个函数的第一个逻辑行的字符串将成为这个函数的文档字符串。注意类和模块同样拥有文档字符串,在后面相应的章节我们会学到它们。

根据惯例:

  1. 文档字符串是一个多行字符串,其中第一行以大写字母开头,并以句号结尾。
  2. 接下来的第二行为空行,从第三行开始为详细的描述。

我强烈建议你在你的正规函数中遵循这个编写文档字符串的惯例。

我们可以通过使用函数的__doc__属性(注意双下划线)存取printMax的文档字符串。如果你在python使用过help(),其实你已经看到过文档字符串的应用了!help()只是取出函数的__doc__属性,然后以一种整洁的方式显示给你。

PEP257 定义了文档字符串的规范。