1. 函数是什么? #
函数 把一组逻辑封装成可复用的代码块 ,通过函数名调用,可传入参数、返回结果。
把「校验订单」「查用户」「发邮件」拆成独立函数,主流程只负责编排,代码更清晰,也便于单元测试。
全栈项目里函数无处不在:视图里调 service、service 里调 DAO、工具模块里的 format_date、hash_password 等。
良好习惯是一个函数只做一件事 、命名见名知意、公共函数写简短 docstring。
def add (a, b ):
return a + b
print (add(3 , 5 ))2. 定义与调用 #
用 def 函数名(参数): 定义函数,函数体缩进 4 空格。
可选的 文档字符串(docstring) 写在函数体第一行,用 """...""" 说明用途、参数与返回值。
函数执行到 return 值 时结束并返回;没有 return 则返回 None 。
调用时写 函数名(实参) ,Python 会按定义顺序把实参绑定到形参,再执行函数体。
下面先给出结构模板,再是具体示例。
def function_name (参数1 , 参数2 ):
"""文档字符串:说明用途(可选)"""
return 结果
def greet (name ):
print (f"Hello, {name} !" )
greet("Alice" )
def get_full_name (first, last ):
return f"{first} {last} " 3. 参数 #
函数通过形参 接收调用方传入的实参 。
Python 支持位置传参、关键字传参、默认值、以及 *args / **kwargs 收集多余参数。
调用时也可用 * / ** 解包列表和字典。
掌握参数规则后,才能读懂 Django/FastAPI 视图里常见的 def view(request, *args, **kwargs)。
3.1 位置参数与关键字参数 #
按位置 传参时,实参顺序必须与形参一致。
用 参数名=值 关键字传参时可打乱顺序,调用长参数列表时可读性更好,也便于只覆盖其中几个参数。
def user_info (name, age, city ):
print (name, age, city)
user_info("Lucy" , 20 , "Beijing" )
user_info(age=20 , name="Lucy" , city="Beijing" )3.2 默认参数 #
形参可带默认值 ,调用时不传该参数则使用默认值。
适合分页 page=1、开关 debug=False 等可选行为。
有默认值的参数必须写在无默认值参数之后 ,否则语法错误。
def greet (name, msg="Hello" ):
print (f"{msg} , {name} !" )
greet("Alice" )
greet("Bob" , msg="Hi" )
def foo (a, b=1 ):
pass
3.3 *args 与 **kwargs #
形参前的 *args 把多出来的位置实参 收进一个元组 。
` kwargs` 把多出来的 关键字实参收进一个 字典**。
用于参数个数不固定的工具函数、装饰器包装原函数、框架透传请求参数等场景。
def log_call (*args, **kwargs ):
print ("args:" , args)
print ("kwargs:" , kwargs)
log_call(1 , 2 , user_id=10 , debug=True )
定义时常见组合:def view(request, *args, **kwargs)。
3.4 调用时解包 #
调用时若已有列表、元组或字典,可用 *序列 、` 字典`** 拆成多个实参传入,键名须与形参名一致。
便于把配置 dict、ORM 字段 dict 直接传给函数,避免逐个写字段名。
def create_user (name, age, city ):
return {"name" : name, "age" : age, "city" : city}
data = {"name" : "Alice" , "age" : 30 , "city" : "Beijing" }
create_user(**data)4. 返回值 #
return 结束函数并把结果交给调用方。
写 return a, b 实际返回的是元组 (a, b),调用方常用 low, high = min_max(nums) 解包。
函数可以多个 return 分支(如校验失败早退),也可以不 return,此时等价于 return None。
业务函数建议明确返回什么:成功返回数据或 True,失败返回 None、错误码或抛异常,避免调用方猜返回值含义。
def min_max (nums ):
return min (nums), max (nums)
low, high = min_max([3 , 1 , 9 ])5. 变量作用域 #
作用域 决定变量在哪些地方可见。
函数内赋值 创建的变量是局部变量 ,出了函数就不可用。
在函数内读取 外部变量时,若未在函数内赋值,会沿作用域链找外层同名变量。
模块顶层定义的是全局变量 。
在函数内要修改 全局变量须显式 global 变量名 ,但业务代码里应尽量少用 global,以免状态难追踪。
下面示例仅作理解作用域之用。
类型
说明
局部
函数内赋值/定义的变量,出了函数就不可用
全局
模块顶层定义的变量
count = 0
def increment ():
global count
count += 1 6. 传递参数 #
函数之间传递数据,首选形参传入、 return 返回 ,数据流清晰、易测。
需要跨多次调用保存状态时,用类实例属性 或显式传入的上下文对象 ,而不是用 global 偷偷改模块级变量。
下面对比推荐写法与不推荐的 global 写法。
def add (a, b ):
return a + b
result = add(3 , 4 )
class Counter :
def __init__ (self ):
self .count = 0
def increment (self ):
self .count += 1
return self .count
counter = Counter()
print (counter.increment())
print (counter.increment())
value = 0
def bad_increment ():
global value
value += 1 7. nonlocal #
nonlocal 用于嵌套函数 (函数里再 def 内层函数)。
在内层要对外层函数的局部变量 重新赋值时,须写 nonlocal x ,否则 Python 会把 x 当作内层新建的局部变量,外层 x 不变。
与 global 不同:nonlocal 只向上找最近一层 enclosing 作用域 ,不指向模块全局。
业务代码里嵌套函数与闭包不如类常见,但读装饰器、工厂函数源码时会遇到。
不用 nonlocal 时,内层对 x 的赋值不会影响外层。
def outer ():
x = 10
def inner ():
nonlocal x
x += 1
return x
inner()
return x
print (outer())
注意 :nonlocal 只能绑定外层嵌套函数的变量;要改模块全局变量仍须用 global。
8. 装饰器 #
装饰器 是在不修改原函数源码 的前提下,在外面包一层逻辑:登录校验、记日志、缓存、限流等。
语法 @decorator 写在 def 上一行,等价于 func = decorator(func) 。
装饰器本身通常是接收函数的函数,内部再定义 wrapper(*args, **kwargs) 并 return wrapper,以兼容不同参数的视图。
current_user = {"username" : "Alice" , "is_authenticated" : True }
def is_logged_in ():
return current_user.get("is_authenticated" , False )
def require_login (func ):
def wrapper (*args, **kwargs ):
print (f"检查用户 {current_user.get('username' , '未知用户' )} 是否已登录..." )
if not is_logged_in():
print ("用户未登录,操作被拒绝" )
raise PermissionError("请先登录" )
print ("用户已登录,正在执行操作..." )
return func(*args, **kwargs)
return wrapper
posts_db = {
1 : {"title" : "第一篇文章" , "author" : "Alice" },
2 : {"title" : "第二篇文章" , "author" : "Bob" }
}
@require_login
def delete_post (post_id ):
print (f"尝试删除ID为 {post_id} 的帖子..." )
if post_id in posts_db:
deleted = posts_db.pop(post_id)
print (f"帖子 '{deleted['title' ]} ' 已被成功删除。" )
return True
else :
print (f"帖子ID {post_id} 不存在,无法删除。" )
return False
@require_login 等价于 delete_post = require_login(delete_post)。
9. lambda #
lambda 定义匿名函数 ,只能包含一个表达式 ,不能写多行语句或赋值。
语法 lambda 参数: 表达式 ,等价于 def 里 return 表达式。
适合作为 sorted、max、min 的 key= 等「很短、只用一次」的回调。
复杂逻辑应写普通 def ,不要堆 lambda。
下面先给出语法形式,再是 add 示例与按字段排序的常见用法。
lambda 参数1 , 参数2 , ... : 表达式
def 函数名 (参数1 , 参数2 , ... ):
return 表达式
add = lambda x, y: x + y
print (add(3 , 5 ))
按字典字段排序是项目里最常见的 lambda 用法:
users = [{"name" : "Alice" , "score" : 85 }, {"name" : "Bob" , "score" : 92 }]
sorted (users, key=lambda u: u["score" ])
10. 项目开发要点 #
写好函数是写好业务的基础。
单一职责 :一个函数只做一件事,便于测试和复用。
命名清晰 :get_user_by_id 优于 func1。
写 docstring :公共函数、接口层建议写一两句说明参数和返回值。
默认参数不要用可变对象 :def f(lst=[]) 会共享同一列表,应写 def f(lst=None): lst = lst or []。
框架里的 *args / **kwargs:路由、中间件、装饰器会透传参数,要能读懂。
装饰器是必备技能 :认证、限流、事务等常通过装饰器或框架内置装饰器实现。
少用递归、闭包工厂 :业务代码里循环/查库更常见;递归易栈溢出。
异常 :在函数内用 try/except 处理可预期错误;不要把所有逻辑都包一层空 except。
11. 总结 #
本章涵盖函数从定义、参数、返回值到作用域、装饰器与 lambda 的核心用法。
日常开发以 def + 清晰参数 + return 为主。
框架里的 @ 与 *args/**kwargs 要能读懂。
下表可作速查。
概念
要点
定义调用
def / return / 缩进
参数
位置、关键字、默认值
可变参数
*args 元组、**kwargs 字典
解包调用
func(*seq)、func(**dict)
多返回值
元组 + 解包赋值
作用域
局部为主,慎用 global
装饰器
@decorator,Web 框架核心
lambda
简单 key=,不宜复杂