作为Python开发者,我经常看到新手在重复编写相同的代码逻辑。函数作为Python编程的核心构建块,其重要性怎么强调都不为过。记得我刚学Python时,曾在一个数据处理脚本中复制粘贴了20多次相同的计算逻辑,后来需求变更时,我不得不逐个修改这些重复代码——这种痛苦经历让我深刻理解了函数的价值。
函数本质上是一个可重用的代码单元,它接收输入(参数),执行特定任务,并返回结果。在Python中,函数是通过def关键字定义的,这个设计源于Python"明确优于隐晦"的哲学理念。当你调用函数时,Python解释器会创建一个新的命名空间(作用域),所有局部变量都存在于这个独立空间中,函数执行完毕后这个空间就会被回收。
当我们写下这样的函数定义时:
python复制def calculate_area(width, height=1):
"""计算矩形面积"""
return width * height
Python解释器会执行以下操作:
有趣的是,def实际上是一个可执行语句。这意味着函数定义是在运行时执行的,这解释了为什么我们可以在条件语句中定义不同的函数:
python复制if condition:
def func():
return "版本A"
else:
def func():
return "版本B"
每次函数调用时,Python都会:
这个机制解释了为什么函数内部的变量不会影响外部同名变量——它们存在于不同的命名空间中。例如:
python复制x = 10 # 全局变量
def modify():
x = 20 # 局部变量
print("函数内:", x)
modify() # 输出: 函数内: 20
print("函数外:", x) # 输出: 函数外: 10
位置参数是最基础的参数传递方式,Python内部使用一个元组来保存这些参数。当调用函数时,解释器会按照定义时的顺序将实参与形参一一对应:
python复制def power(base, exponent):
return base ** exponent
power(2, 3) # 8
这里,2被赋值给base,3被赋值给exponent。如果参数数量不匹配,Python会抛出TypeError。
关键字参数通过参数名显式指定值,这在参数较多或可选参数情况下特别有用:
python复制def create_user(name, age, email, is_admin=False):
# 函数体...
create_user(name="Alice", age=25, email="alice@example.com")
Python内部会将关键字参数转换为字典形式处理。这种方式的优点在于:
默认参数在函数定义时就被求值并存储,这可能导致一个常见陷阱:
python复制def append_to(element, target=[]): # 危险!默认值在定义时就被创建
target.append(element)
return target
print(append_to(1)) # [1]
print(append_to(2)) # [1, 2] 不是预期的[2]
安全做法是使用None作为默认值:
python复制def append_to(element, target=None):
if target is None:
target = []
target.append(element)
return target
*args和**kwargs是Python中处理可变参数的强大工具:
python复制def logger(*args, **kwargs):
print("位置参数:", args)
print("关键字参数:", kwargs)
logger(1, 2, 3, name="Alice", age=25)
实际上:
这种机制使得函数可以接受任意数量和类型的参数,这在装饰器和继承等高级用法中特别有用。
Python函数可以"返回多个值",这实际上是返回一个元组的语法糖:
python复制def get_coordinates():
x = 10
y = 20
return x, y # 等价于 return (x, y)
a, b = get_coordinates() # 元组解包
Python使用LEGB规则查找变量:
python复制x = "global"
def outer():
x = "enclosing"
def inner():
x = "local"
print(x) # 输出"local"
inner()
outer()
闭包是指能够访问其他函数作用域变量的函数:
python复制def counter():
count = 0
def increment():
nonlocal count # 声明count不是局部变量
count += 1
return count
return increment
c = counter()
print(c()) # 1
print(c()) # 2
nonlocal关键字允许我们在嵌套函数中修改外部函数的变量,而不使用global。
在Python中,函数是一等对象,这意味着:
python复制def square(x):
return x * x
# 赋值给变量
func = square
print(func(5)) # 25
# 作为参数传递
def apply(func, value):
return func(value)
print(apply(square, 6)) # 36
lambda用于创建匿名函数,适合简单的单行函数:
python复制# 常规函数
def add(a, b):
return a + b
# lambda等价形式
add = lambda a, b: a + b
lambda常用于排序等高阶函数:
python复制users = [{"name": "Alice", "age": 25}, {"name": "Bob", "age": 20}]
users.sort(key=lambda user: user["age"])
Python提供了几个有用的高阶函数:
python复制# map: 对可迭代对象应用函数
numbers = [1, 2, 3]
squared = list(map(lambda x: x**2, numbers)) # [1, 4, 9]
# filter: 过滤元素
evens = list(filter(lambda x: x % 2 == 0, numbers)) # [2]
# reduce: 累积计算
from functools import reduce
product = reduce(lambda x, y: x * y, numbers) # 6
装饰器本质上是一个接受函数作为参数并返回新函数的函数:
python复制def debug(func):
def wrapper(*args, **kwargs):
print(f"调用 {func.__name__}()")
return func(*args, **kwargs)
return wrapper
@debug
def greet(name):
return f"Hello, {name}"
print(greet("Alice"))
# 输出:
# 调用 greet()
# Hello, Alice
装饰器也可以接受参数,这需要额外嵌套一层函数:
python复制def repeat(times):
def decorator(func):
def wrapper(*args, **kwargs):
for _ in range(times):
result = func(*args, **kwargs)
return result
return wrapper
return decorator
@repeat(3)
def say_hello():
print("Hello")
say_hello()
# 输出:
# Hello
# Hello
# Hello
装饰器也可以使用类实现:
python复制class CountCalls:
def __init__(self, func):
self.func = func
self.calls = 0
def __call__(self, *args, **kwargs):
self.calls += 1
print(f"调用次数: {self.calls}")
return self.func(*args, **kwargs)
@CountCalls
def example():
print("函数执行")
example()
example()
# 输出:
# 调用次数: 1
# 函数执行
# 调用次数: 2
# 函数执行
在循环中调用函数会有额外开销:
python复制# 不推荐
for i in range(10000):
result = calculate(i)
# 推荐: 如果可能,将循环移到函数内部
def batch_calculate():
results = []
for i in range(10000):
results.append(calculate(i))
return results
局部变量访问比全局变量更快:
python复制import math
def slow_calc(values):
result = []
for v in values:
result.append(math.sqrt(v)) # 每次都要查找math模块
return result
def fast_calc(values):
result = []
sqrt = math.sqrt # 局部变量缓存函数
for v in values:
result.append(sqrt(v))
return result
Python 3.5+支持类型提示,可以提高代码可读性:
python复制from typing import List, Tuple
def process_data(data: List[str]) -> Tuple[int, float]:
count = len(data)
avg = sum(len(s) for s in data) / count if count else 0.0
return count, avg
常见错误包括参数顺序错误或缺少必需参数:
python复制def divide(a, b):
return a / b
# 错误示例
divide(10) # 缺少b参数
divide(b=5, a=10) # 正确
如前所述,默认参数在定义时求值,可能导致意外行为:
python复制def add_item(item, items=[]): # 危险!
items.append(item)
return items
print(add_item(1)) # [1]
print(add_item(2)) # [1, 2] 不是预期的[2]
新手常混淆局部变量和全局变量:
python复制x = 10
def modify():
x += 1 # 错误!尝试修改全局变量
print(x)
modify() # UnboundLocalError
正确做法是使用global声明:
python复制x = 10
def modify():
global x
x += 1
print(x)
modify() # 11
装饰器会覆盖原函数的元信息:
python复制def decorator(func):
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper
@decorator
def greet(name):
"""问候函数"""
return f"Hello, {name}"
print(greet.__name__) # wrapper
print(greet.__doc__) # None
解决方案是使用functools.wraps:
python复制from functools import wraps
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper
@decorator
def greet(name):
"""问候函数"""
return f"Hello, {name}"
print(greet.__name__) # greet
print(greet.__doc__) # 问候函数
python复制def process_data(data):
# 数据清洗
cleaned = filter(lambda x: x is not None, data)
# 数据转换
transformed = map(lambda x: x * 2 if isinstance(x, (int, float)) else x, cleaned)
# 数据聚合
result = reduce(lambda acc, x: acc + (x if isinstance(x, (int, float)) else 0),
transformed, 0)
return result
python复制def make_account(initial_balance):
balance = initial_balance
def deposit(amount):
nonlocal balance
balance += amount
return balance
def withdraw(amount):
nonlocal balance
if amount > balance:
raise ValueError("余额不足")
balance -= amount
return balance
def get_balance():
return balance
return {
"deposit": deposit,
"withdraw": withdraw,
"get_balance": get_balance
}
account = make_account(100)
account["deposit"](50) # 150
account["withdraw"](30) # 120
python复制def requires_permission(permission):
def decorator(func):
@wraps(func)
def wrapper(user, *args, **kwargs):
if permission not in user.get("permissions", []):
raise PermissionError("权限不足")
return func(user, *args, **kwargs)
return wrapper
return decorator
@requires_permission("admin")
def delete_user(user, user_id):
# 删除用户逻辑
return f"用户 {user_id} 已删除"
admin_user = {"name": "Admin", "permissions": ["admin"]}
normal_user = {"name": "User", "permissions": ["read"]}
print(delete_user(admin_user, 123)) # 正常工作
print(delete_user(normal_user, 123)) # 抛出PermissionError
每个函数应该只做一件事,并且做好这件事:
python复制# 不推荐
def process_user_data(user):
# 验证数据
if not user.get("name"):
return False
# 保存到数据库
db.save(user)
# 发送欢迎邮件
send_email(user["email"], "Welcome!")
return True
# 推荐
def validate_user(user):
return bool(user.get("name"))
def save_user(user):
db.save(user)
def send_welcome_email(user):
send_email(user["email"], "Welcome!")
def process_user_data(user):
if not validate_user(user):
return False
save_user(user)
send_welcome_email(user)
return True
python复制# 不推荐
def create_user(name, age, email, address, phone, is_admin=False, is_active=True):
pass
# 推荐
def create_user(name, *, age=None, email=None, metadata=None):
"""
name: 必需参数
age, email: 可选关键字参数
metadata: 其他信息的字典
"""
pass
良好的文档应该包括:
python复制def calculate_tax(income: float, brackets: List[Tuple[float, float]]) -> float:
"""
根据给定的税率档次计算应缴税款
参数:
income: 应纳税所得额
brackets: 税率档次列表,每个元素为(起征点, 税率)
返回:
计算得出的税款
示例:
>>> calculate_tax(5000, [(0, 0.1), (3000, 0.2)])
700.0 # 3000*0.1 + 2000*0.2
"""
tax = 0.0
previous_bracket = 0
for bracket in sorted(brackets):
threshold, rate = bracket
if income <= previous_bracket:
break
taxable = min(income, threshold) - previous_bracket
tax += taxable * rate
previous_bracket = threshold
return tax
Python内置调试器pdb可以单步执行函数:
python复制import pdb
def complex_calculation(a, b):
pdb.set_trace() # 设置断点
result = a * b
result += a ** 2
result -= b ** 3
return result
print(complex_calculation(2, 3))
调试命令:
使用cProfile分析函数性能:
python复制import cProfile
def slow_function():
total = 0
for i in range(10000):
for j in range(10000):
total += i * j
return total
profiler = cProfile.Profile()
profiler.runcall(slow_function)
profiler.print_stats()
python复制from timeit import timeit
def test_func():
return sum(range(1000000))
execution_time = timeit(test_func, number=10)
print(f"平均执行时间: {execution_time / 10:.4f}秒")
使用yield关键字创建生成器函数:
python复制def fibonacci_sequence(limit):
"""生成斐波那契数列"""
a, b = 0, 1
while a < limit:
yield a
a, b = b, a + b
for num in fibonacci_sequence(100):
print(num)
生成器特点:
Python协程是更一般的生成器,可以接收数据:
python复制def coroutine_example():
print("协程启动")
while True:
received = yield
print(f"接收到: {received}")
coro = coroutine_example()
next(coro) # 启动协程
coro.send("数据1")
coro.send("数据2")
使用函数实现上下文管理器:
python复制from contextlib import contextmanager
@contextmanager
def managed_resource(path):
print("获取资源")
resource = open(path, 'r')
try:
yield resource
finally:
print("释放资源")
resource.close()
with managed_resource('data.txt') as f:
print(f.read())
python复制class Calculator:
@classmethod
def add(cls, a, b):
return a + b
@staticmethod
def multiply(a, b):
return a * b
print(Calculator.add(2, 3)) # 5
print(Calculator.multiply(2, 3)) # 6
区别:
通过实现__call__方法让实例可像函数一样调用:
python复制class Adder:
def __init__(self, n):
self.n = n
def __call__(self, x):
return self.n + x
add5 = Adder(5)
print(add5(10)) # 15
方法是绑定到类的函数:
python复制class MyClass:
def method(self):
return "实例方法"
@classmethod
def class_method(cls):
return "类方法"
@staticmethod
def static_method():
return "静态方法"
# 函数绑定方式不同
obj = MyClass()
print(obj.method()) # 自动传入self
print(MyClass.class_method()) # 自动传入cls
print(MyClass.static_method()) # 不自动传参
使用types.FunctionType动态创建函数:
python复制import types
def create_function(name, arg_names, code):
# 编译代码
bytecode = compile(code, "<string>", "exec")
# 创建函数对象
globals_dict = {}
exec(bytecode, globals_dict)
func_code = globals_dict[name]
# 创建函数
return types.FunctionType(func_code.__code__, globals(), name,
tuple(arg_names.split(',')))
dynamic_func = create_function("dynamic_add", "a,b", "def dynamic_add(a,b): return a+b")
print(dynamic_func(2,3)) # 5
Python函数是对象,可以检查其属性:
python复制def example(a, b=1):
"""示例函数"""
return a + b
print(example.__name__) # example
print(example.__doc__) # 示例函数
print(example.__code__.co_varnames) # ('a', 'b')
print(example.__defaults__) # (1,)
可以动态修改函数:
python复制def original():
return "原始函数"
def decorator(func):
def wrapper():
return "装饰后的: " + func()
return wrapper
original = decorator(original)
print(original()) # 装饰后的: 原始函数
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)
print(fibonacci(50)) # 非常快,因为有缓存
python复制# 保存为.pyx文件
def cython_fib(int n):
cdef int a=0, b=1, i, temp
for i in range(n):
temp = a
a = b
b = temp + b
return a
编译后可以显著提升性能。
python复制# 不推荐
def slow_func(data):
for item in data:
process(item.attr1, item.attr2)
# 推荐
def fast_func(data):
for item in data:
attr1 = item.attr1
attr2 = item.attr2
process(attr1, attr2)
python复制import unittest
def divide(a, b):
if b == 0:
raise ValueError("除数不能为零")
return a / b
class TestDivide(unittest.TestCase):
def test_divide_normal(self):
self.assertEqual(divide(10, 2), 5)
def test_divide_zero(self):
with self.assertRaises(ValueError):
divide(10, 0)
if __name__ == "__main__":
unittest.main()
python复制def factorial(n):
"""
计算阶乘
>>> factorial(5)
120
>>> factorial(0)
1
>>> factorial(-1)
Traceback (most recent call last):
...
ValueError: n必须是非负整数
"""
if n < 0:
raise ValueError("n必须是非负整数")
return 1 if n == 0 else n * factorial(n-1)
if __name__ == "__main__":
import doctest
doctest.testmod()
python复制import pytest
@pytest.mark.parametrize("a,b,expected", [
(1, 1, 2),
(2, 3, 5),
(0, 0, 0),
])
def test_add(a, b, expected):
assert add(a, b) == expected
python复制import threading
def worker(num):
print(f"工作线程 {num} 开始")
# 执行任务...
print(f"工作线程 {num} 结束")
threads = []
for i in range(5):
t = threading.Thread(target=worker, args=(i,))
threads.append(t)
t.start()
for t in threads:
t.join()
python复制import multiprocessing
def cpu_bound_task(n):
return sum(i * i for i in range(n))
if __name__ == "__main__":
with multiprocessing.Pool() as pool:
results = pool.map(cpu_bound_task, range(10))
print(results)
python复制import asyncio
async def fetch_data(url):
print(f"开始获取 {url}")
await asyncio.sleep(2) # 模拟IO操作
print(f"完成获取 {url}")
return f"{url} 的数据"
async def main():
tasks = [
fetch_data("url1"),
fetch_data("url2"),
fetch_data("url3")
]
results = await asyncio.gather(*tasks)
print(results)
asyncio.run(main())
python复制def process_data(data):
# 不修改原始数据,返回新数据
return [transform(item) for item in data if filter(item)]
python复制from functools import partial
def power(base, exponent):
return base ** exponent
square = partial(power, exponent=2)
cube = partial(power, exponent=3)
print(square(5)) # 25
print(cube(3)) # 27
python复制def compose(*funcs):
def composed(arg):
for f in reversed(funcs):
arg = f(arg)
return arg
return composed
# 创建组合函数
process = compose(str.upper, lambda s: s.strip(), str)
print(process(" hello ")) # "HELLO"
python复制def strategy_add(a, b):
return a + b
def strategy_multiply(a, b):
return a * b
class Calculator:
def __init__(self, strategy=strategy_add):
self.strategy = strategy
def calculate(self, a, b):
return self.strategy(a, b)
calc = Calculator()
print(calc.calculate(2, 3)) # 5
calc.strategy = strategy_multiply
print(calc.calculate(2, 3)) # 6
python复制def create_adder(n):
def adder(x):
return x + n
return adder
add5 = create_adder(5)
add10 = create_adder(10)
print(add5(3)) # 8
print(add10(3)) # 13
python复制def create_observable():
observers = []
def subscribe(observer):
observers.append(observer)
def unsubscribe(observer):
observers.remove(observer)
def notify(message):
for observer in observers:
observer(message)
return {
"subscribe": subscribe,
"unsubscribe": unsubscribe,
"notify": notify
}
observable = create_observable()
def logger(message):
print(f"日志: {message}")
observable["subscribe"](logger)
observable["notify"]("事件发生")
良好的函数组织方式:
python复制# 文件: math_operations.py
def add(a, b):
return a + b
def subtract(a, b):
return a - b
# 文件: main.py
from math_operations import add, subtract
print(add(2, 3))
对于大型项目,使用包组织函数:
code复制my_package/
__init__.py
utils/
__init__.py
math_utils.py
string_utils.py
models/
__init__.py
user.py
product.py
良好的函数接口应该:
python复制def calculate_tax(income, tax_brackets):
"""
计算所得税
参数:
income: 应纳税所得额(浮点数)
tax_brackets: 税率档次列表,每个元素为(起征点, 税率)
返回:
计算得出的税款(浮点数)
异常:
ValueError: 如果income为负数或tax_brackets为空
"""
if income < 0:
raise ValueError("收入不能为负数")
if not tax_brackets:
raise ValueError("必须提供税率档次")
# 计算逻辑...
return tax_amount
python复制def safe_divide(a, b):
try:
return a / b
except ZeroDivisionError:
print("警告: 除数为零")
return float('inf') # 返回无穷大
except TypeError:
print("错误: 参数类型不正确")
raise # 重新抛出异常
python复制class InvalidInputError(Exception):
"""输入无效时抛出"""
pass
def process_input(value):
if not isinstance(value, (int, float)):
raise InvalidInputError("必须是数字")
if value < 0:
raise InvalidInputError("必须是非负数")
return value ** 2
python复制def process_file(path):
try:
with open(path) as f:
data = f.read()
# 处理数据...
except FileNotFoundError:
print(f"文件 {path} 不存在")
except IOError as e:
print(f"读取文件时出错: {e}")
else:
print("处理成功")
finally:
print("清理工作...")
python复制import logging
logging.basicConfig(level=logging.INFO)
def critical_operation(data):
logging.info("开始执行关键操作")
try:
result = perform_operation(data)
logging.info("操作成功完成")
return result
except Exception as e:
logging.error(f"操作失败: {e}")
raise
python复制import time
from functools import wraps
def log_execution_time(func):
@wraps(func)
def wrapper(*args, **kwargs):
start_time = time.perf_counter()
result = func(*args, **kwargs)
end_time = time.perf_counter()
print(f"{func.__name__} 执行时间: {end_time - start_time:.4f}秒")
return result
return wrapper
@log_execution_time
def slow_function():
time.sleep(1)
python复制import logging
import json
def setup_logging():
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
handler = logging.StreamHandler()
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
handler.setFormatter(formatter)
logger.addHandler(handler)
return logger
logger = setup_logging()
def process_data(data):
logger.info("开始处理数据", extra={"data_size": len(data)})
# 处理逻辑...
logger.info("数据处理完成")
python复制def create_greeting(name, config):
greeting = config.get("greeting", "Hello")
punctuation = config.get("punctuation", "!")
return f"{greeting}, {name}{punctuation}"
config = {
"greeting": "Hi",
"punctuation": "!!!"
}
print(create_greeting("Alice", config)) # Hi, Alice!!!
python复制def create_processor(config):
if config["type"] == "uppercase":
def processor(text):
return text.upper()
elif config["type"] == "reverse":
def processor(text):
return text[::-1]
else:
def processor(text):
return text
return processor
processor = create_processor({"type": "uppercase"})
print(processor("hello")) # HELLO
python复制import json
def load_config(path):
with open(path) as f:
return json.load(f)
def configure_function(func, config):
func.config = config
return func
@configure_function
def process_data(data):
if process_data.config.get("verbose"):
print(f"处理数据: {data}")
# 处理逻辑...
config = load_config("config.json")
process_data = configure_function(process_data, config)
python复制def validate_input(*validators):
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
for i, (arg, validator) in enumerate(zip(args, validators)):
if not validator(arg):
raise ValueError(f"参数 {i} 无效")
return func(*args, **kwargs)
return wrapper
return decorator
@validate_input(lambda x: x > 0, lambda x: isinstance(x, str))
def process(positive_num, text):
return text * positive_num
print(process(3, "Hi