10.定义函数

在Python中, 定义一个函数要使用def语句, 一次写出函数名, 括号, 括号中的参数和冒号:, 然后, 在缩进块中编写函数体, 函数的返回值用return语句返回.

我们以自定义一个求绝对值的my_abs函数为例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
"""
title: 自定义函数
Version: 1.0
Author: 李博帅
"""


def my_abs(x):
if x >= 0:
return x
else:
return -x


one = my_abs(10) # 调用函数my_abs, 并传入参数10, 返回值赋予one.
print(one) # 10

two = my_abs(-8) # 调用函数my_abs, 并传入参数-8, 返回值赋予two
print(two) # 8

请注意, 函数体内部的语句在执行时, 一旦执行到reuturn时, 函数就执行完毕, 并将结果返回. 因此, 函数内部通过条件判断和循环可以实现非常复杂的逻辑.

如果没有reture语句, 函数执行完毕后也会返回结果, 只是返回值为None. return noe可以简写为return.

在Python交互环境中定义函数时, 注意Python会出现...的提示. 函数定义结束后结束后需要按两次回车重新回到>>>提示符下:

1
2
3
4
5
6
7
8
>>> def my_abs(x):
... if x >= 0:
... return x
... else:
... return -x
...
>>> my_abs(-9)
9

如果你已经把my_abs()的函数定义保存为abstest.py文件了, 那么, 可以在该文件的当前目录下启动Python解释器, 用from abstest import my_abs来导入my_abs()函数, 注意abstest是文件名 (不含.py扩展名):

1
2
3
>>> from abstest import my_abs                          
>>> my_abs(-9)
9

import的用法在后续模块一节中会详细介绍.

空函数

如果想定义一个什么事业不做的空函数, 可以用pass语句:

1
2
3
4
5
6
7
8
"""
title: 空函数
Version: 1.0
Author: 李博帅
"""

def nop():
pass

pass语句什么都不做, 那有什么用? 实际上pass可以用来作为占位符, 比如现在还没想好怎么写函数的代码, 就可以先放一个pass, 让代码能运行起来.

pass还可以用在其他语句里, 比如:

1
2
3
4
5
6
7
8
9
10
"""
Title: 空函数
Version: 2.0
Author: 李博帅
"""

year = 1999

if year >= 2020:
pass

缺少了pass, 代码运行就会有语法错误.

参数检查

调用函数时, 如果参数个数不对, Python解释器会自己检查出来, 并抛出TypeError:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
"""
Title: 参数检查
Version: 1.0
Author: 李博帅
"""


def my_abs(x):
if x >= 0:
return x
else:
return -x


one = my_abs(-1, 1)

"""
输出结果:

Traceback (most recent call last):
File "C:/Users/j2662/PycharmProjects/one/one.py", line 13, in <module>
one = my_abs(-1, 1)
TypeError: my_abs() takes 1 positional argument but 2 were given
"""

但是如果参数类型 不对, Python解释器就无法帮我们检查. 试试my_abs和内置函数abs的差别:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
"""
Title: 参数检查
Version: 2.0
Author: 李博帅
"""


def my_abs(x):
if x >= 0:
return x
else:
return -x


my_abs('I')

"""
输出结果:

Traceback (most recent call last):
File "C:/Users/j2662/PycharmProjects/one/one.py", line 14, in <module>
my_abs('I')
File "C:/Users/j2662/PycharmProjects/one/one.py", line 9, in my_abs
if x >= 0:
TypeError: '>=' not supported between instances of 'str' and 'int'
"""
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
"""
Title: 参数检查
Version: 3.0
Author: 李博帅
"""

abs('I')

"""
输出结果:

Traceback (most recent call last):
File "C:/Users/j2662/PycharmProjects/one/one.py", line 8, in <module>
abs('I')
TypeError: bad operand type for abs(): 'str'
"""

当传入不恰当的参数时, 内置函数abs会检查出参数错误, 而我们定义的my_abs没有参数检查, 会导致if语句出错, 出错信息和abs不一样. 所以, 这个函数定义不够完善.

让我们修改一下my_abs的定义, 对参数类型作检查, 只允许整数和浮点数类型的参数. 数据类型检查可以用内置函数isinstance()实现. 添加了参数检查后, 如果传入错误的参数类型, 函数就可以跑出一个错误:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
"""
Title: 参数检查
Version: 4.0
Author: 李博帅
"""


def my_abs(x):
if not isinstance(x, (int, float)):
raise TypeError('bad operand type')
if x >= 0:
return x
else:
return -x


my_abs('I')

"""
输出结果:

Traceback (most recent call last):
File "C:/Users/j2662/PycharmProjects/one/one.py", line 16, in <module>
my_abs('I')
File "C:/Users/j2662/PycharmProjects/one/one.py", line 10, in my_abs
raise TypeError('bad operand type')
TypeError: bad operand type
"""

错误和异常处理将在后续讲到.

返回多个值

函数可以返回多个值吗? 答案是肯定的.

比如在游戏中经常需要从一个点移动到另一个点, 给出坐标, 位移和角度, 就可以计算出新的坐标:

1
2
3
4
5
6
7
8
9
10
11
12
13
"""
Title: 返回多个值
Version: 1.0
Author: 李博帅
"""

import math


def move(x, y, step, angle=0):
nx = x + step * math.cos(angle)
ny = y - step * math.sin(angle)
return nx, ny

import math语句表示导入math包, 并允许后续定吗引用math包里的sin,cos等函数.

然后, 我们就可以同时获得返回值:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
"""
Title: 返回多个值
Version: 1.0
Author: 李博帅
"""

import math


def move(x, y, step, angle=0):
nx = x + step * math.cos(angle)
ny = y - step * math.sin(angle)
return nx, ny


x, y = move(100, 100, 60, math.pi / 6)
print(x, y) # 151.96152422706632 70.0

但其实这只是一种就像, Python函数返回的仍然是单一值:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
"""
Title: 返回多个值
Version: 1.0
Author: 李博帅
"""

import math


def move(x, y, step, angle=0):
nx = x + step * math.cos(angle)
ny = y - step * math.sin(angle)
return nx, ny


r = move(100, 100, 60, math.pi / 6)
print(r) # (151.96152422706632, 70.0)

原来返回值是一个 tuple ! 但是, 在语法上, 返回一个 tuple 可以省略括号, 而多个变量可以同时接收一个 tuple, 按位置赋给对应的值, 所以, Python的函数返回多值其实就是返回一个 tuple, 但写起来更方便.

小结

定义函数时, 需要确定函数名和参数个数;

如果有必要, 可以先对参数的数据类型做检查;

函数体内部可以用return随时返回函数结果;

函数执行完毕也没有return语句时, 自动return None;

函数可以同时返回多个值, 但其实就是一个 tuple.

练习

请定义一个函数quadratic(a, b, c), 接收3个参数, 返回一元二次方程$ax^2+bx+c=0$的两个解.

提示:

一元二次方程的求根公式为:
$$
x = \frac {-b\pm\sqrt{b^2-4ac}} {2a}
$$
计算平方根可以调用math.sqrt()函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
"""
Title: 课后练习
Version: 1.0
Author: 李博帅
"""

import math


def quadratic(a, b, c):
if a == 0:
x_one = - (c / b)
x_two = x_one
else:
x_one = (math.sqrt(b ** 2 - 4 * a * c) - b) / (2 * a)
x_two = - (math.sqrt(b ** 2 - 4 * a * c) + b) / (2 * a)
return x_one, x_two


if quadratic(2, 3, 1) != (-0.5, -1.0):
print('测试失败')
elif quadratic(1, 3, -4) != (1.0, -4.0):
print('测试失败')
else:
print('测试成功')

print('quadratic(2, 3, 1) = ', quadratic(2, 3, 1))
print('quadratic(1, 3, -4) = ', quadratic(1, 3, -4))

"""
输出结果:

测试成功
quadratic(2, 3, 1) = (-0.5, -1.0)
quadratic(1, 3, -4) = (1.0, -4.0)
"""

本文是整合了”廖雪峰”老师的Python基础教程. 当然也有本人站在初学者角度上, 更便于萌新学习, 做了一些改动. 想观摩”廖雪峰”老师原文的, 可以点下面链接.

廖雪峰的官方网站

Thank you for your accept. mua!
-------------本文结束感谢您的阅读-------------