6.3 函数定义

深入学习Python函数的定义方法,掌握def关键字的使用,理解函数参数检查的重要性,学会使用isinstance进行类型检查,掌握返回单个值和多个值的实现方式,了解空函数和pass语句的使用场景。

Python里定义函数用 def 关键字,后面跟函数名、括号里的参数,再加个冒号。函数体写在缩进块里,想返回什么就用 return

拿求绝对值来说,我们可以写个 my_abs 函数:

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

print(my_abs(-99))

你可以自己跑一下代码,看看 -99 的绝对值是不是 99。

这里有个关键点:函数执行到 return 的时候就立马结束了,结果直接返回。所以配合 if 判断和循环,你能实现各种复杂的逻辑。

要是函数里没写 return 呢?照样会返回结果,只不过是 None。其实 return None 可以直接简写成 return

在Python交互环境里定义函数时,你会看到 ... 这个提示符。定义完了记得按两次回车,才能回到 >>> 提示符:

┌─────────────────────────────────────────────────────────┐
Windows PowerShell                                 -  x 
├─────────────────────────────────────────────────────────┤
>>> 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 导入函数。注意 abstest 是文件名,不带 .py 后缀:

┌─────────────────────────────────────────────────────────┐
Windows PowerShell                                 -  x 
├─────────────────────────────────────────────────────────┤
>>> from abstest import my_abs                           
>>> my_abs(-9)                                           
9                                                        
>>>                                                      
                                                         
└─────────────────────────────────────────────────────────┘

关于 import 的更多用法,后面讲模块的时候会详细说。

空函数

有时候你知道要写个函数,但还没想好具体怎么实现,这时候就可以用 pass 占个位:

def nop():
    pass

pass 字面意思就是"啥也不干",但它的作用是让代码先跑起来,避免语法错误。你可以之后再慢慢填充函数内容。

pass 在其他地方也能用,比如条件判断:

if age >= 18:
    pass

少了 pass 的话,Python会报语法错误。

参数检查

调用函数时参数个数不对,Python会直接报错:

>>> my_abs(1, 2)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: my_abs() takes 1 positional argument but 2 were given

但参数类型不对就麻烦了,Python不会自动帮你检查。看看 my_abs 和内置的 abs 有什么区别:

>>> my_abs('A')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in my_abs
TypeError: unorderable types: str() >= int()
>>> abs('A')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: bad operand type for abs(): 'str'

传个字符串进去,内置的 abs 会明确告诉你"参数类型不对",而我们的 my_abs 直接在 if 判断那里挂了,错误信息也不够清楚。所以这个函数还不够完善。

改进一下,加上参数类型检查,只接受整数和浮点数:

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('A')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in my_abs
TypeError: bad operand type

错误处理和异常机制后面会专门讲。

返回多个值

函数能不能返回多个值?当然可以。

比如游戏里角色移动,给定当前坐标、移动距离和角度,就能算出新坐标:

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 是导入数学库,这样就能用 sincos 这些函数了。

调用的时候可以同时拿到两个返回值:

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

不过这有个小秘密——Python函数实际上还是只返回一个值:

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

看到没?返回的其实是个 tuple!Python语法允许你省略括号直接写 return nx, ny,而接收的时候可以用多个变量按位置拆开。所以"返回多个值"只是个语法糖,本质还是返回一个tuple,但写起来方便多了。

练习

试着写个 quadratic(a, b, c) 函数,求解一元二次方程 ax²+bx+c=0 的两个根。

提示一下,求根公式是:

x=2a−b±b2−4ac

平方根可以用 math.sqrt() 函数:

>>> import math
>>> math.sqrt(2)
1.4142135623730951
import math

def quadratic(a, b, c):
    pass

# 测试:
print('quadratic(2, 3, 1) =', quadratic(2, 3, 1))
print('quadratic(1, 3, -4) =', quadratic(1, 3, -4))

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

小结

定义函数要确定函数名和参数个数。有必要的话,先检查一下参数类型。函数体里随时可以用 return 返回结果,要是执行完了也没遇到 return,就自动返回 None

函数能同时返回多个值,但其实返回的是一个 tuple。