在Python中,函数就像厨房里的多功能料理机——你把食材(参数)放进去,选择功能(函数体),它就能产出你想要的结果。我刚开始学Python时,最惊讶的就是用def这个简单的关键字就能创建如此强大的工具。
函数的核心价值在于:
来看个最简单的例子:
python复制def greet(name):
"""打招呼的函数"""
return f"Hello, {name}!"
print(greet("Alice")) # 输出:Hello, Alice!
新手常犯的错误是忘记return语句。如果函数没有显式return,它会默认返回None,这可能导致后续代码出现意想不到的bug。
python复制def power(base, exponent):
return base ** exponent
print(power(2, 3)) # 8
python复制print(power(exponent=3, base=2)) # 同样输出8
python复制def power(base, exponent=2):
return base ** exponent
print(power(3)) # 9,使用默认exponent=2
默认参数有个"坑":默认值只会在函数定义时计算一次。当默认值是可变对象(如列表、字典)时,可能导致意外行为:
python复制def add_item(item, lst=[]):
lst.append(item)
return lst
print(add_item(1)) # [1]
print(add_item(2)) # [1, 2] 而不是预期的[2]
当参数数量不确定时,这两个特殊参数就派上用场了:
python复制def logger(*args, **kwargs):
print("位置参数:", args)
print("关键字参数:", kwargs)
logger(1, 2, 3, name="Alice", age=25)
# 输出:
# 位置参数: (1, 2, 3)
# 关键字参数: {'name': 'Alice', 'age': 25}
实际应用场景:
闭包就像是一个带着"记忆"的函数——它能记住自己被创建时的环境。这是我花了最长时间才真正理解的概念:
python复制def outer_func(x):
def inner_func(y):
return x + y
return inner_func
closure = outer_func(10)
print(closure(5)) # 15
这里inner_func记住了x=10这个值,即使outer_func已经执行完毕。闭包的典型应用:
新手容易混淆的是变量作用域。Python使用LEGB规则查找变量:
lambda就像函数的"快餐版"——简单场景下可以快速定义小函数:
python复制square = lambda x: x ** 2
print(square(5)) # 25
但要注意:
实际应用场景:
虽然列表推导式现在更常用,但了解这些函数式工具很有必要:
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))
from functools import reduce
# reduce:累积计算
sum_all = reduce(lambda x, y: x+y, numbers)
性能提示:在Python 3中,这些函数返回的是迭代器而非列表,所以需要list()转换才能看到结果。对于大数据集,这种惰性求值方式更节省内存。
装饰器是我认为Python最优雅的特性之一。它允许在不修改原函数代码的情况下增加功能:
python复制def timer(func):
def wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
end = time.time()
print(f"{func.__name__}执行耗时:{end-start:.4f}秒")
return result
return wrapper
@timer
def heavy_calculation(n):
return sum(i*i for i in range(n))
heavy_calculation(1000000)
常见装饰器应用:
python复制def greet(name: str) -> str:
return f"Hello, {name}"
当函数出现问题时,我常用的调试方法:
python复制def complex_func(x):
print(f"输入参数:{x}") # 调试点1
intermediate = step1(x)
print(f"中间结果:{intermediate}") # 调试点2
return step2(intermediate)
python复制import pdb
def buggy_func():
x = 1
pdb.set_trace() # 在这里暂停
y = x + "2" # 故意制造TypeError
return y
python复制import logging
logging.basicConfig(level=logging.DEBUG)
def critical_func():
logging.debug("函数开始执行")
try:
# 业务代码
except Exception as e:
logging.error(f"执行出错:{str(e)}")
raise
python复制# 不好的实现
def factorial(n):
return 1 if n == 0 else n * factorial(n-1)
# 更好的实现
def factorial(n):
result = 1
for i in range(1, n+1):
result *= i
return result
python复制from functools import lru_cache
@lru_cache(maxsize=128)
def fibonacci(n):
if n < 2:
return n
return fibonacci(n-1) + fibonacci(n-2)
python复制# 不好的做法
def process_data(data):
results = []
for item in data:
processed = expensive_operation(item)
results.append(processed.upper())
return results
# 更好的做法
def process_data(data):
return [expensive_operation(item).upper() for item in data]
当处理大量数据时,生成器函数可以显著减少内存使用:
python复制def read_large_file(file_path):
with open(file_path, 'r') as f:
for line in f:
yield line.strip()
# 使用方式
for line in read_large_file('huge_data.txt'):
process(line) # 一次只处理一行,不加载整个文件到内存
生成器的关键特点:
在实际项目中,我通常会创建utils.py存放常用函数:
python复制# utils.py
import os
from datetime import datetime
def get_file_size(file_path):
"""获取文件大小(MB)"""
size = os.path.getsize(file_path) / (1024 * 1024)
return round(size, 2)
def format_timestamp(ts, fmt='%Y-%m-%d %H:%M:%S'):
"""格式化时间戳"""
return datetime.fromtimestamp(ts).strftime(fmt)
def validate_email(email):
"""简单的邮箱验证"""
return '@' in email and '.' in email.split('@')[-1]
这样设计的好处:
通过函数实现轻量级的接口模式:
python复制def data_exporter_factory(format):
"""工厂函数返回不同的导出函数"""
exporters = {
'csv': export_to_csv,
'json': export_to_json,
'excel': export_to_excel
}
return exporters.get(format.lower(), export_to_csv)
def export_to_csv(data):
# 实现CSV导出逻辑
pass
def export_to_json(data):
# 实现JSON导出逻辑
pass
# 使用示例
exporter = data_exporter_factory('json')
exporter(sales_data)
这种模式的优势:
使用pytest为函数编写测试:
python复制# test_math_utils.py
from math_utils import add, divide
def test_add_positive_numbers():
assert add(2, 3) == 5
def test_add_negative_numbers():
assert add(-1, -1) == -2
def test_divide_normal_case():
assert divide(10, 2) == 5
def test_divide_by_zero():
import pytest
with pytest.raises(ValueError):
divide(10, 0)
测试要点:
良好的docstring应该包含:
python复制def calculate_compound_interest(principal, rate, years):
"""
计算复利利息
Args:
principal (float): 本金金额
rate (float): 年利率(如0.05表示5%)
years (int): 投资年数
Returns:
float: 最终本息合计
Raises:
ValueError: 如果参数为负数
Examples:
>>> calculate_compound_interest(1000, 0.05, 10)
1628.894626777442
"""
if principal < 0 or rate < 0 or years < 0:
raise ValueError("参数不能为负数")
return principal * (1 + rate) ** years
推荐使用Google风格或NumPy风格的docstring,方便生成API文档。