定义函数
函数是 Python 中最基本的代码组织单元。def 关键字会创建一个函数对象,并将其与当前命名空间中的名称绑定。理解函数定义背后的机制,比单纯记住语法更重要。
def 语句的完整语法
函数定义以 def 开头,后跟函数名、圆括号中的形参列表和冒号。函数体从新一行开始,必须缩进:
def greet(name):
"""向用户问好。"""
print(f"你好,{name}!")
函数名遵循与变量相同的命名规则:只能包含字母、数字和下划线,不能以数字开头。按惯例,函数名使用小写加下划线(snake_case)。函数名可以与内置函数同名(如 def list():),但这会遮蔽内置名称,极不推荐。
函数定义是一条可执行语句。这意味着你可以在 if 分支、while 循环甚至另一个函数内部定义函数:
condition = True
if condition:
def helper():
return "条件为真时的辅助函数"
else:
def helper():
return "条件为假时的辅助函数"
print(helper()) # 条件为真时的辅助函数
函数对象与多重绑定
执行 def 语句时,Python 会创建一个函数对象(类型为 function),并将其地址赋给函数名。函数对象包含代码、文档字符串、默认参数值、注解等信息。函数名只是指向这个对象的引用,因此可以像任何其他对象一样被传递:
def square(x):
return x * x
# 函数对象可以赋值给其他变量
f = square
print(f(5)) # 25
print(f is square) # True
# 可以放入列表、字典等容器中
funcs = [square, abs, len]
print(funcs[0](3)) # 9
# 可以作为其他函数的参数
def apply_twice(func, value):
return func(func(value))
print(apply_twice(square, 2)) # 16
函数对象有一个 __name__ 属性,记录定义时的名称:
print(square.__name__) # 'square'
f = square
print(f.__name__) # 仍然是 'square'
函数命名空间与调用机制
每次调用函数时,Python 都会创建一个新的局部命名空间(局部符号表)。函数内部的所有变量赋值都进入这个局部命名空间;引用变量时,解释器按 LEGB 规则查找:先局部、再外层函数局部、再全局、最后内置。
x = "全局变量"
def demo():
y = "局部变量" # y 进入 demo 的局部命名空间
print(x) # x 不在局部,去全局找,输出"全局变量"
print(y) # y 在局部,输出"局部变量"
demo()
# print(y) # NameError: name 'y' is not defined
实参传递的本质是对象引用传递:调用时,实参对象的引用被复制到被调用函数的局部符号表中。如果实参是可变对象(如列表、字典),函数内部通过引用修改对象内容,调用者会看到变化;如果实参是不可变对象(如整数、字符串),函数内部重新赋值只会改变局部引用,不影响外部。
def append_item(lst, item):
lst.append(item) # 原地修改可变对象,外部可见
def rebind_list(lst):
lst = [99] # 局部变量重新绑定,外部不可见
my_list = [1, 2]
append_item(my_list, 3)
print(my_list) # [1, 2, 3]
rebind_list(my_list)
print(my_list) # [1, 2, 3],未被改变
无返回值与 None
Python 函数始终有返回值。没有 return 语句,或 return 后面没有表达式时,函数返回 None。None 是内置名称,表示"什么都没有",类型为 NoneType。
def no_return():
print("执行完毕")
result = no_return()
print(result) # None
print(result is None) # True
def explicit_none():
return
print(explicit_none()) # None
初学者常犯的错误是把 print 和 return 混淆。print 向标准输出写内容,但返回 None;return 把值传回调用处。
def wrong():
print(42) # 只是打印,不返回
def right():
return 42 # 把 42 传回调用处
x = wrong() # 终端显示 42,但 x 是 None
y = right() # y 是 42
函数定义的执行时机
函数体在定义时不会执行,只在调用时执行。但函数定义行中的默认参数表达式会在定义时求值:
i = 5
def f(arg=i): # 定义时 i 是 5,默认值被固定为 5
print(arg)
i = 6
f() # 输出 5,不是 6
这个特性对可变默认参数有深远影响,详见《默认参数》文档。
常见错误
函数体未缩进会报 IndentationError:
def broken():
print("未缩进") # IndentationError
在 return 后写不可达代码会触发警告(但不会报错):
def unreachable(x):
return x * 2
print("这行永远不会执行") # 语法上合法,逻辑上无意义
忘记括号会把函数对象本身当作返回值,而不是调用结果:
def get_value():
return 42
print(get_value) # <function get_value at 0x...>
print(get_value()) # 42
小结
def 创建函数对象并绑定名称;调用时新建局部命名空间;实参按对象引用传递;无 return 时返回 None。理解这些机制,是掌握 Python 函数编程的第一步。