函数#
函数是组织代码的重要方式,可以将代码封装成可重用的块。Python 提供了强大的函数功能,包括参数传递、作用域、装饰器等。
函数定义和调用#
定义函数#
# 基本语法
def function_name(parameters):
"""文档字符串(可选)"""
# 函数体
return value # 可选
# 示例
def greet(name):
"""打印问候语"""
print(f"Hello, {name}!")
# 调用函数
greet("Python") # 输出: Hello, Python!函数文档字符串#
def add(a, b):
"""
计算两个数的和
参数:
a: 第一个数
b: 第二个数
返回:
两个数的和
"""
return a + b
# 访问文档字符串
print(add.__doc__)
help(add)返回值#
# 返回单个值
def square(x):
return x ** 2
result = square(5) # 25
# 返回多个值(实际上是返回元组)
def get_name_age():
return "Alice", 30
name, age = get_name_age() # 元组解包
result = get_name_age() # ('Alice', 30)
# 没有return语句,返回None
def do_nothing():
pass
result = do_nothing() # None参数传递#
位置参数#
位置参数按照定义顺序传递。
def greet(name, age):
print(f"{name} is {age} years old")
greet("Alice", 25) # Alice is 25 years old
greet(25, "Alice") # 25 is Alice years old(顺序错误!)关键字参数#
使用参数名传递,可以不按顺序。
def greet(name, age):
print(f"{name} is {age} years old")
greet(name="Alice", age=25) # Alice is 25 years old
greet(age=25, name="Alice") # Alice is 25 years old(顺序可以改变)
greet("Alice", age=25) # Alice is 25 years old(混合使用)默认参数#
函数参数可以有默认值。
def greet(name, age=18):
print(f"{name} is {age} years old")
greet("Alice") # Alice is 18 years old(使用默认值)
greet("Bob", 30) # Bob is 30 years old(覆盖默认值)
# 默认参数必须是不可变对象(重要!)
def add_item(item, my_list=[]): # 危险!默认参数是可变对象
my_list.append(item)
return my_list
# 正确的方式
def add_item(item, my_list=None):
if my_list is None:
my_list = []
my_list.append(item)
return my_list*args - 可变位置参数#
*args 用于接收任意数量的位置参数,以元组形式传递。
def sum_numbers(*args):
"""计算所有参数的和"""
total = 0
for num in args:
total += num
return total
result = sum_numbers(1, 2, 3) # 6
result = sum_numbers(1, 2, 3, 4, 5) # 15
# args是一个元组
def print_args(*args):
print(type(args)) # <class 'tuple'>
print(args)
print_args(1, 2, 3) # (1, 2, 3)
# 解包传递
numbers = [1, 2, 3]
sum_numbers(*numbers) # 等价于 sum_numbers(1, 2, 3)**kwargs - 可变关键字参数#
**kwargs 用于接收任意数量的关键字参数,以字典形式传递。
def print_info(**kwargs):
"""打印所有关键字参数"""
for key, value in kwargs.items():
print(f"{key}: {value}")
print_info(name="Alice", age=30, city="Beijing")
# 输出:
# name: Alice
# age: 30
# city: Beijing
# kwargs是一个字典
def print_kwargs(**kwargs):
print(type(kwargs)) # <class 'dict'>
print(kwargs)
print_kwargs(a=1, b=2) # {'a': 1, 'b': 2}
# 解包传递
info = {"name": "Alice", "age": 30}
print_info(**info) # 等价于 print_info(name="Alice", age=30)参数顺序规则#
函数参数必须按照以下顺序定义:
- 位置参数
*args- 关键字参数(有默认值的)
**kwargs
def example(pos1, pos2, *args, kw1="default", kw2="default", **kwargs):
pass
# 调用示例
example(1, 2, 3, 4, kw1="value1", extra="value2")
# pos1=1, pos2=2, args=(3, 4), kw1="value1", kw2="default", kwargs={"extra": "value2"}作用域和命名空间(重点难点)#
什么是命名空间#
命名空间(namespace)是名字到对象的映射,可理解为「名字 → 对象」的字典。不同命名空间中的同名名字互不干扰。
常见类型:
| 类型 | 说明 | 示例 |
|---|---|---|
| 内置 | 解释器启动时加载 | len, print, open |
| 全局 | 模块级名字 | 模块顶层的变量、函数、类 |
| 局部 | 函数/类体内名字 | 函数参数、函数内赋值的变量 |
| 闭包 | 嵌套函数的外层名字 | 外层函数的局部名字 |
命名空间与作用域:作用域是某段代码能访问的命名空间范围;LEGB 规则即名字查找时访问命名空间的顺序(见下)。
局部作用域和全局作用域#
# 全局变量
x = 10
def my_function():
# 局部变量
y = 20
print(x) # 可以访问全局变量
print(y) # 可以访问局部变量
my_function()
# print(y) # 错误!y是局部变量,外部无法访问global 关键字#
使用 global 关键字可以在函数内部修改全局变量,数字为不可更改对象,修改全局作用域的变量绑定
x = 10
def modify_global():
global x # 声明x是全局变量
x = 20 # 修改全局变量
modify_global()
print(x) # 20
# 不使用global的情况
x = 10
def try_modify():
x = 20 # 这创建了一个新的局部变量x,不是修改全局变量
try_modify()
print(x) # 10(全局变量未改变)nonlocal 关键字#
nonlocal 用于在嵌套函数中修改外层函数的变量。
def outer():
x = 10
def inner():
nonlocal x # 声明x是外层函数的变量
x = 20 # 修改外层函数的变量
inner()
print(x) # 20
outer()
# 不使用nonlocal的情况
def outer():
x = 10
def inner():
x = 20 # 这创建了一个新的局部变量x
inner()
print(x) # 10(外层变量未改变)
outer()LEGB 规则#
Python 按以下顺序在命名空间中查找名字(LEGB):
- Local(局部作用域)
- Enclosing(嵌套作用域)
- Global(全局作用域)
- Built-in(内置作用域)
# 示例
x = "global"
def outer():
x = "enclosing"
def inner():
x = "local"
print(x) # local(局部作用域)
inner()
print(x) # enclosing
outer()
print(x) # globallambda 匿名函数#
lambda 用于创建简单的匿名函数。
# 基本语法:lambda 参数: 表达式
square = lambda x: x ** 2
print(square(5)) # 25
# 等价于
def square(x):
return x ** 2
# 多个参数
add = lambda x, y: x + y
print(add(2, 3)) # 5
# 常用场景:作为参数传递
numbers = [1, 2, 3, 4, 5]
squared = list(map(lambda x: x ** 2, numbers)) # [1, 4, 9, 16, 25]
# 排序
students = [("Alice", 25), ("Bob", 20), ("Charlie", 30)]
students.sort(key=lambda x: x[1]) # 按年龄排序函数装饰器(重点难点)#
装饰器是 Python 的高级特性,用于在不修改原函数的情况下扩展函数功能。
装饰器的工作原理#
装饰器的本质是函数替换。当使用 @decorator 语法时,Python 会执行以下步骤:
- 定义阶段:Python 先定义被装饰的函数
- 装饰阶段:立即将函数对象传递给装饰器函数
- 替换阶段:用装饰器返回的新函数替换原函数
关键理解:
- 装饰器在函数定义时立即执行,而不是在函数调用时
- 装饰器接收函数对象作为参数,返回一个新的可调用对象
- 原函数被替换为装饰器返回的新函数
def decorator(func):
print(f"装饰器执行,装饰函数: {func.__name__}") # 在函数定义时执行
def wrapper():
print("函数调用时执行")
return func()
return wrapper
@decorator
def test():
print("原函数执行")
# 输出: 装饰器执行,装饰函数: test(在定义时立即执行)
test() # 输出: 函数调用时执行 \n 原函数执行装饰器的闭包机制#
装饰器利用了 Python 的闭包特性。内部函数 wrapper 可以访问外部函数 decorator 的参数 func,即使外部函数已经返回。
def my_decorator(func):
# func 被闭包捕获,保存在 wrapper 的 __closure__ 中
def wrapper():
print("Before")
func() # 访问外部函数的参数 func
print("After")
return wrapper
@my_decorator
def say_hello():
print("Hello")
# 查看闭包
print(say_hello.__closure__) # (<cell at 0x...: function object at 0x...>,)
print(say_hello.__closure__[0].cell_contents) # <function say_hello at 0x...>递归函数#
递归函数是调用自身的函数。
# 计算阶乘
def factorial(n):
if n == 0 or n == 1:
return 1
return n * factorial(n - 1)
result = factorial(5) # 120
# 斐波那契数列
def fibonacci(n):
if n <= 1:
return n
return fibonacci(n - 1) + fibonacci(n - 2)
result = fibonacci(6) # 8
# 递归深度限制
import sys
print(sys.getrecursionlimit()) # 默认1000函数作为对象#
在 Python 中,函数是一等对象,可以作为参数传递、赋值等。
def greet(name):
return f"Hello, {name}!"
# 函数可以赋值给变量
my_func = greet
print(my_func("Python")) # Hello, Python!
# 函数可以作为参数传递
def apply_func(func, value):
return func(value)
result = apply_func(greet, "World") # Hello, World!
# 函数可以作为返回值
def get_greeter():
def greeter(name):
return f"Hello, {name}!"
return greeter
greeter = get_greeter()
print(greeter("Python")) # Hello, Python!内置函数#
Python 提供了许多内置函数,常用的有:
# 类型转换
int("123")
float("3.14")
str(123)
list("abc")
tuple([1, 2, 3])
# 数学函数
abs(-5) # 5
round(3.14159, 2) # 3.14
min(1, 2, 3) # 1
max(1, 2, 3) # 3
sum([1, 2, 3]) # 6
# 序列操作
len("hello") # 5
sorted([3, 1, 2]) # [1, 2, 3]
reversed([1, 2, 3]) # 迭代器
enumerate(["a", "b"]) # 迭代器
zip([1, 2], ["a", "b"]) # 迭代器
# 其他
isinstance(10, int) # True
type(10) # <class 'int'>
hasattr(obj, "attr") # 检查对象是否有属性
getattr(obj, "attr", default) # 获取属性函数式编程工具#
# map() - 对序列的每个元素应用函数
numbers = [1, 2, 3, 4]
squared = list(map(lambda x: x**2, numbers)) # [1, 4, 9, 16]
# filter() - 过滤序列
numbers = [1, 2, 3, 4, 5]
evens = list(filter(lambda x: x % 2 == 0, numbers)) # [2, 4]
# reduce() - 累积计算(需要导入)
from functools import reduce
numbers = [1, 2, 3, 4]
product = reduce(lambda x, y: x * y, numbers) # 24最佳实践#
- 函数应该只做一件事
- 使用有意义的函数名
- 添加文档字符串
- 避免使用可变对象作为默认参数
- 合理使用装饰器
- 注意作用域和命名空间
下一步#
掌握了函数后,可以学习: