用户工具

站点工具


python:第三章函数

第三章 函数

本章概要

函数是组织好的、可重复使用的代码块,用于实现单一或相关联的功能。本章将深入学习Python函数的定义、参数传递、返回值、lambda表达式和装饰器等高级特性。

3.1 函数基础

3.1.1 定义函数

使用 ``def`` 关键字定义函数。

# 基本函数定义
def greet():
    """这是一个问候函数"""
    print("Hello, World!")
 
# 调用函数
greet()  # Hello, World!

3.1.2 函数参数

# 带参数的函数
def greet_person(name):
    """向指定的人问候"""
    print(f"Hello, {name}!")
 
greet_person("Alice")  # Hello, Alice!
greet_person("Bob")    # Hello, Bob!
 
# 多个参数
def add(a, b):
    """返回两个数的和"""
    return a + b
 
result = add(3, 5)
print(result)  # 8

3.1.3 返回值

# 返回单个值
def square(x):
    return x ** 2
 
# 返回多个值(实际是返回元组)
def get_min_max(numbers):
    return min(numbers), max(numbers)
 
minimum, maximum = get_min_max([3, 1, 4, 1, 5, 9])
print(f"最小值: {minimum}, 最大值: {maximum}")
 
# 没有return语句,默认返回None
def print_message(msg):
    print(msg)
 
result = print_message("Hello")
print(result)  # None

3.1.4 文档字符串

def calculate_area(length, width):
    """
    计算矩形的面积。
 
    参数:
        length (float): 矩形的长度
        width (float): 矩形的宽度
 
    返回:
        float: 矩形的面积
 
    示例:
        >>> calculate_area(5, 3)
        15
    """
    return length * width
 
# 查看文档字符串
print(calculate_area.__doc__)
help(calculate_area)

3.2 参数类型

3.2.1 位置参数

按照位置顺序传递参数。

def power(base, exponent):
    return base ** exponent
 
print(power(2, 3))   # 8 (2的3次方)
print(power(3, 2))   # 9 (3的2次方)

3.2.2 关键字参数

通过参数名传递,可以任意顺序。

def introduce(name, age, city):
    print(f"我叫{name},今年{age}岁,来自{city}")
 
# 关键字参数
introduce(age=25, city="北京", name="张三")
 
# 混合使用(位置参数必须在关键字参数之前)
introduce("李四", city="上海", age=30)

3.2.3 默认参数值

def greet(name, greeting="Hello"):
    print(f"{greeting}, {name}!")
 
greet("Alice")                    # Hello, Alice!
greet("Bob", "Hi")                # Hi, Bob!
greet("Charlie", greeting="Hey")  # Hey, Charlie!

⚠️ 警告:默认参数的陷阱

# 错误示例
def append_item(item, item_list=[]):
    item_list.append(item)
    return item_list
 
print(append_item(1))  # [1]
print(append_item(2))  # [1, 2] - 意外!列表被保留了
 
# 正确做法
def append_item(item, item_list=None):
    if item_list is None:
        item_list = []
    item_list.append(item)
    return item_list
 
print(append_item(1))  # [1]
print(append_item(2))  # [2] - 正确

3.2.4 可变参数 *args

接收任意数量的位置参数。

def sum_all(*args):
    """计算所有参数的和"""
    total = 0
    for num in args:
        total += num
    return total
 
# 调用方式
print(sum_all(1, 2, 3))      # 6
print(sum_all(1, 2, 3, 4, 5)) # 15
print(sum_all())             # 0
 
# args是一个元组
print(type(args))  # <class 'tuple'>

3.2.5 关键字可变参数 **kwargs

接收任意数量的关键字参数。

def print_info(**kwargs):
    """打印所有关键字参数"""
    for key, value in kwargs.items():
        print(f"{key}: {value}")
 
print_info(name="Alice", age=25, city="北京")
# 输出:
# name: Alice
# age: 25
# city: 北京
 
# kwargs是一个字典
print(type(kwargs))  # <class 'dict'>

3.2.6 参数顺序规则

正确的参数顺序:

def func(pos1, pos2, /, pos_or_kwd, *, kwd1, kwd2, **kwargs):
    """
    参数顺序:
    1. 仅限位置参数 (/ 之前)
    2. 位置或关键字参数 (/ 和 * 之间)
    3. 仅限关键字参数 (* 之后)
    4. **kwargs
    """
    pass
 
# 示例
def combined(a, b, /, c, d, *args, e, f, **kwargs):
    print(f"a={a}, b={b}, c={c}, d={d}")
    print(f"args={args}")
    print(f"e={e}, f={f}")
    print(f"kwargs={kwargs}")
 
combined(1, 2, 3, 4, 5, 6, e=7, f=8, g=9, h=10)

3.2.7 解包参数

# 使用*解包列表/元组
def add(a, b, c):
    return a + b + c
 
numbers = [1, 2, 3]
print(add(*numbers))  # 6
 
# 使用**解包字典
def create_user(name, age, email):
    return {"name": name, "age": age, "email": email}
 
user_data = {"name": "Alice", "age": 25, "email": "alice@example.com"}
user = create_user(**user_data)
print(user)

3.3 变量作用域

3.3.1 局部变量和全局变量

# 全局变量
global_var = "我在函数外"
 
def demo():
    # 局部变量
    local_var = "我在函数内"
    print(local_var)     # 可以访问局部变量
    print(global_var)    # 可以访问全局变量
 
demo()
print(global_var)        # 可以访问全局变量
# print(local_var)       # 错误!不能访问局部变量

3.3.2 global 关键字

在函数内修改全局变量。

counter = 0
 
def increment():
    global counter
    counter += 1
    print(f"计数器: {counter}")
 
increment()  # 计数器: 1
increment()  # 计数器: 2
print(counter)  # 2

3.3.3 nonlocal 关键字

在嵌套函数中修改外层(非全局)变量。

def outer():
    x = "外部"
 
    def inner():
        nonlocal x
        x = "内部修改"
        print(f"inner: {x}")
 
    inner()
    print(f"outer: {x}")
 
outer()
# 输出:
# inner: 内部修改
# outer: 内部修改

3.3.4 作用域规则(LEGB)

Python查找变量的顺序:

1. **L**ocal - 局部作用域
2. **E**nclosing - 嵌套函数的外层作用域
3. **G**lobal - 全局作用域
4. **B**uilt-in - 内置作用域
x = "global"  # Global
 
def outer():
    x = "enclosing"  # Enclosing
 
    def inner():
        x = "local"  # Local
        print(x)     # 输出: local
 
    inner()
    print(x)         # 输出: enclosing
 
outer()
print(x)             # 输出: global

3.4 高级函数特性

3.4.1 函数作为对象

在Python中,函数是一等对象,可以像其他对象一样传递。

def greet(name):
    return f"Hello, {name}!"
 
# 将函数赋值给变量
say_hello = greet
print(say_hello("Alice"))  # Hello, Alice!
 
# 函数作为参数
def execute_function(func, arg):
    return func(arg)
 
result = execute_function(greet, "Bob")
print(result)  # Hello, Bob!
 
# 函数作为返回值
def create_multiplier(n):
    def multiplier(x):
        return x * n
    return multiplier
 
double = create_multiplier(2)
triple = create_multiplier(3)
 
print(double(5))  # 10
print(triple(5))  # 15

3.4.2 闭包

闭包是引用了外部变量但在外部作用域执行的函数。

def make_power(exponent):
    """创建幂函数"""
    def power(base):
        return base ** exponent
    return power
 
square = make_power(2)
cube = make_power(3)
 
print(square(4))  # 16
print(cube(2))    # 8
 
# 查看闭包信息
print(square.__closure__)  # 查看闭包单元
print(square.__closure__[0].cell_contents)  # 2 (exponent的值)

3.4.3 匿名函数(lambda)

lambda用于创建小型匿名函数。

# 基本语法
square = lambda x: x ** 2
print(square(5))  # 25
 
# 多个参数
add = lambda x, y: x + y
print(add(3, 4))  # 7
 
# 默认参数
greet = lambda name, greeting="Hello": f"{greeting}, {name}!"
print(greet("Alice"))       # Hello, Alice!
print(greet("Bob", "Hi"))   # Hi, Bob!

常用场景

# 作为高阶函数的参数
numbers = [1, 2, 3, 4, 5]
 
# map()
squares = list(map(lambda x: x ** 2, numbers))
print(squares)  # [1, 4, 9, 16, 25]
 
# filter()
evens = list(filter(lambda x: x % 2 == 0, numbers))
print(evens)  # [2, 4]
 
# sorted()的key参数
students = [("Alice", 85), ("Bob", 92), ("Charlie", 78)]
sorted_by_score = sorted(students, key=lambda x: x[1], reverse=True)
print(sorted_by_score)  # [('Bob', 92), ('Alice', 85), ('Charlie', 78)]
 
# reduce()
from functools import reduce
product = reduce(lambda x, y: x * y, numbers)
print(product)  # 120

3.4.4 递归函数

函数调用自身。

# 阶乘
def factorial(n):
    if n <= 1:
        return 1
    return n * factorial(n - 1)
 
print(factorial(5))  # 120
 
# 斐波那契数列
def fibonacci(n):
    if n <= 1:
        return n
    return fibonacci(n - 1) + fibonacci(n - 2)
 
print([fibonacci(i) for i in range(10)])  # [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
 
# 尾递归优化(Python不支持尾递归优化)
def factorial_tail(n, acc=1):
    if n <= 1:
        return acc
    return factorial_tail(n - 1, n * acc)
 
print(factorial_tail(5))  # 120

3.4.5 装饰器

装饰器是修改函数行为的强大工具。

基本装饰器

def my_decorator(func):
    def wrapper(*args, **kwargs):
        print("函数执行前")
        result = func(*args, **kwargs)
        print("函数执行后")
        return result
    return wrapper
 
@my_decorator
def say_hello():
    print("Hello!")
 
say_hello()
# 输出:
# 函数执行前
# Hello!
# 函数执行后

带参数的装饰器

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(times=3)
def greet(name):
    print(f"Hello, {name}!")
 
greet("Alice")
# 输出三次: Hello, Alice!

使用functools.wraps

from functools import wraps
 
def my_decorator(func):
    @wraps(func)  # 保留原函数的元数据
    def wrapper(*args, **kwargs):
        """包装函数"""
        print("执行函数...")
        return func(*args, **kwargs)
    return wrapper
 
@my_decorator
def example():
    """示例函数"""
    print("Hello")
 
print(example.__name__)  # example (不使用wraps会显示wrapper)
print(example.__doc__)   # 示例函数

常用装饰器示例

import time
from functools import wraps
 
# 计时装饰器
def timer(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        start = time.time()
        result = func(*args, **kwargs)
        elapsed = time.time() - start
        print(f"{func.__name__} 执行时间: {elapsed:.4f}秒")
        return result
    return wrapper
 
# 缓存装饰器(简单的memoization)
def memoize(func):
    cache = {}
    @wraps(func)
    def wrapper(*args):
        if args not in cache:
            cache[args] = func(*args)
        return cache[args]
    return wrapper
 
@timer
@memoize
def fib(n):
    if n < 2:
        return n
    return fib(n - 1) + fib(n - 2)
 
print(fib(30))

3.5 函数式编程工具

3.5.1 map()

将函数应用于可迭代对象的每个元素。

numbers = [1, 2, 3, 4, 5]
 
# 使用lambda
squares = list(map(lambda x: x ** 2, numbers))
 
# 使用内置函数
lengths = list(map(len, ["apple", "banana", "cherry"]))
 
# 多个可迭代对象
sums = list(map(lambda x, y: x + y, [1, 2, 3], [4, 5, 6]))
print(sums)  # [5, 7, 9]

3.5.2 filter()

根据条件筛选元素。

numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
 
# 筛选偶数
evens = list(filter(lambda x: x % 2 == 0, numbers))
 
# 筛选非空字符串
strings = ["hello", "", "world", "", "python"]
non_empty = list(filter(None, strings))
print(non_empty)  # ['hello', 'world', 'python']

3.5.3 reduce()

累积计算。

from functools import reduce
 
numbers = [1, 2, 3, 4, 5]
 
# 求和
sum_result = reduce(lambda x, y: x + y, numbers)
print(sum_result)  # 15
 
# 求积
product = reduce(lambda x, y: x * y, numbers)
print(product)  # 120
 
# 找最大值
maximum = reduce(lambda x, y: x if x > y else y, numbers)
print(maximum)  # 5
 
# 带初始值
sum_with_initial = reduce(lambda x, y: x + y, numbers, 100)
print(sum_with_initial)  # 115

3.5.4 zip()

# 并行迭代
names = ["Alice", "Bob", "Charlie"]
ages = [25, 30, 35]
 
for name, age in zip(names, ages):
    print(f"{name}: {age}")
 
# 创建字典
keys = ["a", "b", "c"]
values = [1, 2, 3]
d = dict(zip(keys, values))
print(d)  # {'a': 1, 'b': 2, 'c': 3}
 
# 处理不等长序列
from itertools import zip_longest
list1 = [1, 2, 3]
list2 = ["a", "b"]
for x, y in zip_longest(list1, list2, fillvalue="-"):
    print(x, y)

3.5.5 其他工具函数

# enumerate()
for i, char in enumerate("Python", start=1):
    print(f"{i}: {char}")
 
# sorted()
numbers = [3, 1, 4, 1, 5, 9]
sorted_numbers = sorted(numbers)
reversed_numbers = sorted(numbers, reverse=True)
 
# all() / any()
print(all([True, True, False]))  # False
print(any([False, True, False])) # True
print(all(x > 0 for x in [1, 2, 3]))  # True
 
# reversed()
for char in reversed("Python"):
    print(char, end="")  # nohtyP

3.6 代码示例

示例1:缓存装饰器

from functools import wraps
 
def lru_cache(maxsize=128):
    """简单的LRU缓存装饰器"""
    def decorator(func):
        cache = {}
        order = []
 
        @wraps(func)
        def wrapper(*args):
            if args in cache:
                # 移动到最近使用
                order.remove(args)
                order.append(args)
                return cache[args]
 
            result = func(*args)
            cache[args] = result
            order.append(args)
 
            # 超出限制,删除最久未使用的
            if len(cache) > maxsize:
                oldest = order.pop(0)
                del cache[oldest]
 
            return result
 
        return wrapper
    return decorator
 
@lru_cache(maxsize=3)
def fibonacci(n):
    if n < 2:
        return n
    return fibonacci(n - 1) + fibonacci(n - 2)
 
print(fibonacci(50))

示例2:函数注册器

class FunctionRegistry:
    """函数注册器 - 用于注册和调用函数"""
 
    def __init__(self):
        self._functions = {}
 
    def register(self, name=None):
        """装饰器:注册函数"""
        def decorator(func):
            func_name = name or func.__name__
            self._functions[func_name] = func
            return func
        return decorator
 
    def call(self, name, *args, **kwargs):
        """调用已注册的函数"""
        if name not in self._functions:
            raise ValueError(f"未知函数: {name}")
        return self._functions[name](*args, **kwargs)
 
    def list_functions(self):
        """列出所有注册的函数"""
        return list(self._functions.keys())
 
# 使用示例
registry = FunctionRegistry()
 
@registry.register()
def add(a, b):
    return a + b
 
@registry.register("multiply")
def mul(a, b):
    return a * b
 
print(registry.list_functions())  # ['add', 'multiply']
print(registry.call("add", 3, 4))      # 7
print(registry.call("multiply", 3, 4)) # 12

3.7 练习题

练习1:计算圆的相关值

编写函数,根据半径计算圆的面积和周长。

import math
 
def circle_properties(radius):
    """返回圆的面积和周长"""
    area = math.pi * radius ** 2
    circumference = 2 * math.pi * radius
    return area, circumference
 
# 测试
r = 5
area, circumference = circle_properties(r)
print(f"半径为{r}的圆:")
print(f"  面积: {area:.2f}")
print(f"  周长: {circumference:.2f}")

练习2:验证器装饰器

编写装饰器验证函数参数是否为正数。

from functools import wraps
 
def validate_positive(func):
    """验证所有参数为正数"""
    @wraps(func)
    def wrapper(*args, **kwargs):
        for arg in args:
            if isinstance(arg, (int, float)) and arg <= 0:
                raise ValueError(f"参数必须为正数,得到: {arg}")
        return func(*args, **kwargs)
    return wrapper
 
@validate_positive
def rectangle_area(width, height):
    return width * height
 
print(rectangle_area(5, 3))  # 15
# print(rectangle_area(-5, 3))  # ValueError

练习3:柯里化

实现函数柯里化(将多参数函数转为单参数函数链)。

from functools import partial
 
def curry(func):
    """柯里化装饰器"""
    def curried(*args, **kwargs):
        if len(args) + len(kwargs) >= func.__code__.co_argcount:
            return func(*args, **kwargs)
        return partial(curried, *args, **kwargs)
    return curried
 
@curry
def add_three(a, b, c):
    return a + b + c
 
# 使用
print(add_three(1)(2)(3))      # 6
print(add_three(1, 2)(3))      # 6
print(add_three(1)(2, 3))      # 6
 
add_five = add_three(5)
print(add_five(10)(20))        # 35

练习4:函数组合

实现函数组合(compose)。

from functools import reduce
 
def compose(*functions):
    """函数组合: compose(f, g, h)(x) = f(g(h(x)))"""
    def composed(x):
        return reduce(lambda v, f: f(v), reversed(functions), x)
    return composed
 
# 使用
add_one = lambda x: x + 1
double = lambda x: x * 2
square = lambda x: x ** 2
 
transform = compose(square, double, add_one)
# 等价于: square(double(add_one(x)))
print(transform(3))  # (3+1)*2 squared = 64

本章小结

本章学习了Python函数的核心概念:

  • 函数定义 - def、参数、返回值、文档字符串
  • 参数类型 - 位置参数、关键字参数、默认参数、*args、kwargs * 作用域 - 局部变量、全局变量、global、nonlocal、LEGB规则 * 高级特性 - 函数作为对象、闭包、lambda、递归 * 装饰器 - 基本装饰器、带参数的装饰器、常用装饰器模式 * 函数式编程** - map、filter、reduce、zip等工具函数

掌握函数后,你可以编写模块化、可重用的代码了。下一章将学习Python的内置数据结构。

进一步阅读

python/第三章函数.txt · 最后更改: 127.0.0.1