函数是Python编程中最基础也最重要的概念之一。简单来说,函数就是一段可重复使用的代码块,它接收输入(参数),处理这些输入,然后返回结果。这种封装不仅让代码更整洁,还能大大提高开发效率。
在Python中,函数使用def关键字定义,后面跟着函数名和括号内的参数列表。函数体需要缩进,这是Python语法的重要特点。例如:
python复制def greet(name):
return f"Hello, {name}!"
这个简单的greet函数接收一个name参数,返回拼接好的问候语。调用时只需写greet("Alice"),就会返回"Hello, Alice!"。
注意:Python函数名应该使用小写字母,单词间用下划线连接,这是PEP 8风格指南的建议。避免使用Python内置函数名作为自定义函数名,如不要定义名为print()的函数。
函数可以没有参数,也可以有多个参数。参数可以是任何Python对象 - 数字、字符串、列表、字典,甚至是其他函数。Python是动态类型语言,所以不需要声明参数类型,这使得函数定义非常灵活。
Python中的参数传递实际上是"按对象引用传递"。这意味着当你调用函数并传递参数时,实际上传递的是对象的引用(可以理解为内存地址),而不是对象本身的副本。
考虑这个例子:
python复制def modify_list(lst):
lst.append(4)
my_list = [1, 2, 3]
modify_list(my_list)
print(my_list) # 输出[1, 2, 3, 4]
这里my_list在函数调用后被修改了,因为传递的是列表的引用,函数内部操作的是同一个列表对象。但对于不可变对象(如数字、字符串、元组),函数内部对参数的修改不会影响外部变量。
Python函数使用return语句返回值。一个函数可以有多个return语句,但执行到第一个return就会退出函数。如果不写return语句,函数默认返回None。
python复制def is_even(num):
if num % 2 == 0:
return True
else:
return False
这个函数检查数字是否为偶数。实际上可以简化为return num % 2 == 0,因为比较表达式本身就会返回True或False。
实操技巧:当函数需要返回多个值时,可以返回一个元组,然后在调用处解包:
python复制def get_user(): return "Alice", 25, "alice@example.com" name, age, email = get_user()
良好的函数应该包含文档字符串(docstring),用三引号括起来,描述函数的功能、参数和返回值:
python复制def calculate_area(length, width):
"""
计算矩形面积
参数:
length (float): 矩形的长度
width (float): 矩形的宽度
返回:
float: 矩形的面积
"""
return length * width
文档字符串可以通过help(calculate_area)查看,是代码自文档化的重要手段。
Python允许为参数指定默认值,调用时可以不传递这些参数:
python复制def greet(name, greeting="Hello"):
return f"{greeting}, {name}!"
可以这样调用:
python复制greet("Alice") # 使用默认greeting
greet("Bob", "Hi") # 覆盖默认值
注意事项:默认参数在函数定义时计算并保存,因此不应该将可变对象(如列表、字典)作为默认值,否则所有调用会共享同一个对象:
python复制def add_item(item, lst=[]): # 错误做法 lst.append(item) return lst
调用函数时,可以显式指定参数名,这样参数的顺序就不重要了:
python复制def describe_pet(pet_name, animal_type="dog"):
print(f"I have a {animal_type} named {pet_name}.")
describe_pet(animal_type="hamster", pet_name="Harry")
Python支持两种可变参数:
*args:接收任意数量的位置参数,打包成元组**kwargs:接收任意数量的关键字参数,打包成字典python复制def make_pizza(*toppings):
print("Making pizza with:")
for topping in toppings:
print(f"- {topping}")
make_pizza("pepperoni")
make_pizza("mushrooms", "green peppers", "extra cheese")
函数内部定义的变量是局部变量,只在函数内有效。函数外部定义的变量是全局变量,整个程序都可以访问。
python复制x = "global" # 全局变量
def func():
y = "local" # 局部变量
print(x) # 可以访问全局变量
print(y)
func()
print(y) # 报错,y未定义
要在函数内修改全局变量,需要使用global声明:
python复制count = 0
def increment():
global count
count += 1
对于嵌套函数,要修改外层函数的变量,使用nonlocal:
python复制def outer():
x = "outer"
def inner():
nonlocal x
x = "inner"
inner()
print(x) # 输出"inner"
Python支持闭包(closure),即内部函数可以记住并访问外部函数的变量,即使外部函数已经执行完毕:
python复制def make_multiplier(factor):
def multiplier(x):
return x * factor
return multiplier
double = make_multiplier(2)
print(double(5)) # 输出10
闭包在装饰器和回调函数中非常有用。
lambda用于创建匿名函数,适合简单的操作:
python复制square = lambda x: x ** 2
print(square(5)) # 输出25
lambda常用于需要函数作为参数的场合,如排序:
python复制points = [(1, 2), (3, 1), (5, 0)]
points.sort(key=lambda point: point[1]) # 按y坐标排序
这些是函数式编程的常用工具:
python复制numbers = [1, 2, 3, 4, 5]
# map: 对每个元素应用函数
squares = list(map(lambda x: x**2, numbers))
# filter: 过滤元素
evens = list(filter(lambda x: x % 2 == 0, numbers))
# reduce: 累积计算
from functools import reduce
product = reduce(lambda x, y: x * y, numbers)
使用yield关键字可以创建生成器函数,它返回一个可迭代对象:
python复制def countdown(n):
while n > 0:
yield n
n -= 1
for i in countdown(5):
print(i)
生成器在内存使用上非常高效,特别适合处理大数据集。
装饰器本质上是一个函数,它接收一个函数作为参数并返回一个新函数:
python复制def my_decorator(func):
def wrapper():
print("Before function call")
func()
print("After function call")
return wrapper
@my_decorator
def say_hello():
print("Hello!")
say_hello()
装饰器也可以接收参数,这需要再加一层嵌套:
python复制def repeat(num_times):
def decorator(func):
def wrapper(*args, **kwargs):
for _ in range(num_times):
result = func(*args, **kwargs)
return result
return wrapper
return decorator
@repeat(num_times=3)
def greet(name):
print(f"Hello {name}")
greet("Alice")
装饰器也可以实现为类,只要实现__call__方法:
python复制class CountCalls:
def __init__(self, func):
self.func = func
self.num_calls = 0
def __call__(self, *args, **kwargs):
self.num_calls += 1
print(f"Call {self.num_calls} of {self.func.__name__}")
return self.func(*args, **kwargs)
@CountCalls
def say_hello():
print("Hello!")
say_hello()
say_hello()
在实际项目中,我经常使用装饰器来统一处理日志记录、性能测量和权限检查等功能。这种设计让核心业务逻辑保持简洁,同时又能方便地添加横切关注点。例如,可以用一个@timing装饰器自动记录函数执行时间:
python复制import time
def timing(func):
def wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
end = time.time()
print(f"{func.__name__} took {end-start:.2f} seconds")
return result
return wrapper
@timing
def slow_function():
time.sleep(2)
slow_function()