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 是导入数学库,这样就能用 sin、cos 这些函数了。
调用的时候可以同时拿到两个返回值:
>>> 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。