讲解
函数是可复用的代码块,用 def 定义:def greet(name): 后面缩进的代码块就是函数体,调用时写 greet('小明')。函数用 return 返回值,可以同时返回多个值(本质是返回元组,调用方解包接收);没有 return 语句的函数返回 None。和变量一样,函数名也是蛇形命名:calculate_total、send_email。定义之后函数名就是一个普通的名字,可以赋值、可以放进列表——Python 里函数是「一等公民」。
一个容易踩的概念是「定义时不执行,调用才执行」:def 只是把代码块存起来并贴上名字,函数体里的错误(比如拼错的变量名)要到调用那一刻才暴露。参数传递方面,Python 传的是「对象引用」:函数内对参数重新赋值不影响外部,但如果参数是可变对象(列表、字典),函数内修改它的内容会影响外部——函数应该改传入的列表还是返回新列表,要在设计时想清楚并写进文档字符串。
文档字符串(docstring)是函数体第一行的三引号字符串,用来说明函数做什么、参数和返回值是什么,help(函数名) 和各种 IDE 都会展示它。好的函数遵循「单一职责」:一个函数只做一件事,名字说清这件事;行数控制在几十行以内,太长就拆分。类型注解(type hints)是现代 Python 的推荐写法:def add(a: int, b: int) -> int: 不影响运行,但让编辑器和类型检查工具能帮你抓错。
示例
# 定义与调用
def greet(name):
"""向指定名字的人问好。"""
return f'你好,{name}!'
print(greet('小明'))
print(greet('ohmydocs 读者'))
# 返回多个值:本质是元组,调用方解包
def analyze(scores):
"""返回分数列表的(最高分, 最低分, 平均分)。"""
return max(scores), min(scores), sum(scores) / len(scores)
high, low, avg = analyze([88, 75, 92, 60])
print(f'最高 {high},最低 {low},平均 {avg:.1f}')
# 没有 return 就是 None
def log(message):
print('[日志]', message)
result = log('程序启动')
print('返回值:', result)
# 函数是一等公民:可以放进列表、作为参数传递
def shout(text):
return text.upper() + '!'
def whisper(text):
return text.lower() + '...'
for func in [shout, whisper]:
print(func('Python'))
# 可变参数:函数内修改会影响外部
def append_tag(tags, tag):
tags.append(tag) # 改的是传入的同一个列表
return tags
my_tags = ['python']
append_tag(my_tags, 'tutorial')
print('外部列表也变了:', my_tags)
# 类型注解:不影响运行,帮助工具和你自己
def add(a: int, b: int) -> int:
return a + b
print('注解不影响运行:', add(2, 3), add('左', '右'))
常见坑
- def 后面忘加括号或冒号:def greet name 是语法错误;定义永远带括号,即使无参数:def greet():。
- 调用了还没定义的名字:Python 自上而下执行,函数必须先定义后调用(主流程在文件顶部调用文件底部的 def 会报 NameError)。
- 在函数里改可变参数又返回它,调用方搞不清新旧:统一约定——要么纯函数返回新对象,要么就地修改返回 None,别两头都占。
- 用 print 代替 return:print 只是展示,值没法被后续代码使用。函数要产出结果就 return,print 只用于调试和最终展示。
小结
def 定义、return 返回、多返回值靠元组解包;函数是一等公民;可变参数的内容修改会影响外部。下一节把参数的花样一次讲透。