Python:函数(一)

python函数相关的知识点

1. 函数定义与调用

定义 :使用 def 关键字,后接函数名和参数列表。

python 复制代码
def greet(name):
    """打印问候语(文档字符串)"""
    print(f"Hello, {name}!")

调用

python 复制代码
greet("Alice")  # 输出:Hello, Alice!

2. 参数与返回值

  • 位置参数:按顺序传递参数。
  • 默认参数:为参数提供默认值。
python 复制代码
def power(base, exponent=2):
    return base ​** exponent

print(power(3))      # 输出:9(使用默认exponent=2)
print(power(2, 4))   # 输出:16
  • 返回值 :使用 return 返回结果,无返回值时默认返回 None
python 复制代码
def add(a, b):
    return a + b

result = add(3, 5)  # result = 8
  1. 参数传递的基本机制

Python中所有参数的传递都是 ​对象引用的传递

  • 不可变对象(如整数、字符串、元组):函数内对形参的修改会创建新对象,不影响实参。

  • 可变对象​(如列表、字典):函数内对形参的修改会影响实参(因为它们指向同一对象)。解决:传副本

示例

python 复制代码
def modify(a, b,c):
    a = 2       # 修改不可变对象(创建新对象)
    b.append(3) # 修改可变对象
    c.append(4)

x = 1          # 不可变
y = [1, 2]     # 可变
c=[1,2,3]
modify(x, y,c.copy())

print(x)       # 输出:1(未改变)
print(y)       # 输出:[1, 2, 3](已改变)
print(c)       # 输出:[1, 2, 3](不改变)

  1. 参数传递的方式

Python支持多种参数传递方式,具体取决于函数定义和调用时的语法。

(1) ​位置参数(Positional Arguments)​

按参数定义顺序传递,最常见的方式。

python 复制代码
def add(a, b):
    return a + b

print(add(3, 5))  # 输出:8

(2) ​关键字参数(Keyword Arguments)​

通过参数名指定值,顺序可以打乱。

python 复制代码
print(add(b=5, a=3))  # 输出:8

(3) ​默认参数(Default Arguments)​

为参数提供默认值,调用时可省略。

python 复制代码
def greet(name, msg="Hello"):
    print(f"{msg}, {name}!")

greet("Alice")          # 输出:Hello, Alice!
greet("Bob", "Hi")      # 输出:Hi, Bob!

(4) ​可变参数(*args 和 ​kwargs)​**

  • *args:接收任意数量的位置参数(元组形式)(写最后)。

  • **kwargs:接收任意数量的关键字参数(字典形式)(写最后)。

python 复制代码
def print_all(*args, ​**kwargs):
    print("位置参数:", args)
    print("关键字参数:", kwargs)

print_all(1, 2, name="Alice", age=25)
# 输出:
# 位置参数: (1, 2)
# 关键字参数: {'name': 'Alice', 'age': 25}

  1. 参数解包(Unpacking)​

在调用函数时,可以使用 *** 解包序列或字典作为参数。

示例

python 复制代码
def func(a, b, c):
    print(a, b, c)

# 解包列表/元组(按位置传递)
args = [1, 2, 3]
func(*args)  # 输出:1 2 3

# 解包字典(按关键字传递)
kwargs = {"a": 1, "b": 2, "c": 3}
func(**kwargs)  # 输出:1 2 3

  1. 注意事项

(1) ​默认参数的陷阱

默认参数的值在函数定义时计算一次,若默认值是可变对象,多次调用会共享该对象。

python 复制代码
def append_to(item, lst=[]):
    lst.append(item)
    return lst

# 第一次调用
print(append_to(1))  # 输出:[1]
# 第二次调用
print(append_to(2))  # 输出:[1, 2](而不是预期的[2])

原因分析

  • Python 在函数定义时(即代码加载时)就创建了默认参数 lst=[],并将其存储在函数对象的 __defaults__ 属性中。
  • 所有未显式传递 lst 参数的调用,都会共享同一个列表对象
  • 每次调用 append_to 时,修改的是同一个列表,而不是创建新列表。

修正方法 :使用不可变对象(如 None)占位。

python 复制代码
def append_to(item, lst=None):
    if lst is None:
        lst = []  # 每次调用时创建新列表
    lst.append(item)
    return lst

# 第一次调用
print(append_to(1))  # 输出:[1]
# 第二次调用
print(append_to(2))  # 输出:[2](符合预期)

关键点

  • 默认参数设为 None(不可变对象)。
  • 在函数内部检查 lst is None,若为真则创建一个新的空列表。
  • 每次调用都会生成新的列表对象,避免共享问题。

默认参数是字典、集合等可变对象时,同样需要警惕:

python 复制代码
# 错误示例:默认值为空字典
def update_dict(key, value, d={}):
    d[key] = value
    return d

print(update_dict("a", 1))  # {'a': 1}
print(update_dict("b", 2))  # {'a': 1, 'b': 2}

# 正确写法
def update_dict(key, value, d=None):
    if d is None:
        d = {}
    d[key] = value
    return d

通过 __defaults__ 属性查看函数的默认参数值:

python 复制代码
def func(a, lst=[]):
    pass

print(func.__defaults__)  # 输出:([],)

# 调用函数后,默认列表被修改
func(1)
print(func.__defaults__)  # 输出:([1],)
  • 规则:默认参数的值在函数定义时被计算一次,并存储在函数对象中。
  • 陷阱:若默认值是可变对象(列表、字典等),所有未显式传递该参数的调用会共享同一个对象。
  • 解决 :用 None 作为默认值,在函数内部初始化可变对象。
python 复制代码
# 错误写法 ❌
def func(arg=[]):
    pass

# 正确写法 ✅
def func(arg=None):
    if arg is None:
        arg = []

(2) ​避免意外修改可变对象

若函数需要处理可变对象但不希望影响原始数据,应创建副本。

python 复制代码
def process_list(lst):
    lst = lst.copy()  # 创建副本
    lst.append(100)
    return lst

original = [1, 2, 3]
modified = process_list(original)
print(original)  # [1, 2, 3](未被修改)
print(modified)   # [1, 2, 3, 100]

  1. 总结
参数类型 特点
位置参数 按顺序传递,简单直接
关键字参数 按参数名指定,提高可读性
默认参数 提供默认值,简化调用
可变参数(*args 接收任意数量的位置参数
可变参数(**kwargs 接收任意数量的关键字参数
参数解包 使用 *** 将序列或字典解包为参数

返回值

  1. 基本返回值

使用 return 语句返回结果。如果没有 returnreturn 后无值,函数默认返回 None

python 复制代码
def add(a, b):
    return a + b

result = add(3, 5)
print(result)  # 输出:8

# 无返回值的函数
def greet(name):
    print(f"Hello, {name}!")

result = greet("Alice")  # 输出:Hello, Alice!
print(result)            # 输出:None

  1. 返回多个值

Python 允许通过 ​返回元组 实现多值返回,调用时可以直接解包。

python 复制代码
def min_max(numbers):
    return min(numbers), max(numbers)

values = [4, 2, 9, 7]
min_val, max_val = min_max(values)#解包
print(f"最小值: {min_val}, 最大值: {max_val}")  # 输出:最小值: 2, 最大值: 9

本质

函数返回的多个值实际上是一个元组,解包是隐式操作:

python 复制代码
result = min_max(values)
print(result)  # 输出:(2, 9)

  1. 返回复杂对象

函数可以返回列表、字典、类实例等任意对象。

示例 1:返回字典

python 复制代码
def create_person(name, age):
    return {"name": name, "age": age}

person = create_person("Bob", 30)
print(person)  # 输出:{'name': 'Bob', 'age': 30}

示例 2:返回函数(闭包)

python 复制代码
def outer():
    def inner():
        print("内部函数被调用")
    return inner

func = outer()
func()  # 输出:内部函数被调用
  1. 返回生成器(yield)​

使用 yield 关键字定义生成器函数,返回一个生成器对象,支持迭代。

python 复制代码
def count_up_to(max):
    count = 1
    while count <= max:
        yield count
        count += 1

generator = count_up_to(3)
for num in generator:
    print(num)  # 依次输出:1, 2, 3

  1. 返回 None 的情况

以下情况函数返回 None

  1. 没有 return 语句。

  2. return 语句没有指定返回值。

  3. return 后仅写 None

python 复制代码
def func1():
    pass

def func2():
    return

def func3():
    return None

print(func1())  # None
print(func2())  # None
print(func3())  # None

  1. 提前返回与多条件返回

函数中可以存在多个 return 语句,执行到第一个 return 时函数终止。

示例:根据条件返回不同结果

python 复制代码
def check_number(num):
    if num < 0:
        return "负数"
    elif num == 0:
        return "零"
    else:
        return "正数"

print(check_number(-5))  # 输出:负数
print(check_number(0))   # 输出:零
print(check_number(10))  # 输出:正数

  1. 注意事项

(1) ​返回值与可变对象

如果返回的是可变对象(如列表、字典),多次调用可能共享同一对象(需谨慎)。

python 复制代码
def get_list():
    return []

list1 = get_list()
list2 = get_list()
list1.append(1)
print(list2)  # 输出:[](安全,因为每次返回新列表)

# 但若函数内部缓存了可变对象:
def get_cached_list(lst=[]):
    return lst

list3 = get_cached_list()
list4 = get_cached_list()
list3.append(1)
print(list4)  # 输出:[1](危险!共享同一列表)

(2) ​类型提示(Python 3+)​

可以为返回值添加类型注解,提高代码可读性。

python 复制代码
def add(a: int, b: int) -> int:
    return a + b

  1. 总结
返回类型 示例 说明
单个值 return 10 直接返回数据
多个值(元组解包) return a, b 返回元组,调用时可解包
复杂对象 return {"data": [...]} 返回字典、列表、实例等
函数或闭包 return inner 返回内部函数,形成闭包
生成器 yield value 返回生成器,支持惰性计算
None returnreturn None 默认返回值

合理使用返回值可以让函数更灵活,例如:

  • 通过返回生成器处理大数据集(节省内存)。

  • 返回函数实现装饰器或策略模式。

  • 返回元组简化多值传递。

3. 作用域与 global

  • 函数内部默认不能修改全局变量,需使用 global 声明。
python 复制代码
x = 10

def modify_global():
    global x
    x = 20

modify_global()
print(x)  # 输出:20

5. 嵌套函数、闭包与装饰器

  • 嵌套函数:在函数内部定义另一个函数。
  • 闭包:内部函数记住外部作用域的变量。
python 复制代码
def outer(msg):
    def inner():
        print(msg)  # 闭包保留外部变量msg
    return inner

closure = outer("Hello")
closure()  # 输出:Hello

装饰器用于增强函数功能,不修改原函数代码。

python 复制代码
def my_decorator(func):
    def wrapper():
        print("装饰器:函数执行前")
        func()
        print("装饰器:函数执行后")
    return wrapper

@my_decorator
def say_hello():
    print("Hello!")

say_hello()
# 输出:
# 装饰器:函数执行前
# Hello!
# 装饰器:函数执行后

在 Python 中,​闭包(Closure)​ 和 ​装饰器(Decorator)​ 是两个紧密关联且强大的概念,它们通过函数的高阶特性实现代码复用和动态扩展功能。


一、闭包(Closure)

  1. 定义

闭包是 ​一个函数与其外部作用域变量的绑定关系。即使外部函数已执行完毕,内部函数仍能访问和修改外部函数的变量。

  1. 核心机制
  • 捕获变量:内部函数会"记住"外部函数的作用域(即闭包环境)。

  • 延迟绑定:闭包中的变量引用在函数调用时才解析,可能导致意外行为(需注意循环变量问题)。

  1. 示例
python 复制代码
def outer(msg):
    def inner():
        print(msg)  # inner捕获了外部变量msg
    return inner

closure = outer("Hello")
closure()  # 输出:Hello(即使outer函数已执行完毕,msg仍被保留)
  1. 实际应用
  • 工厂函数:动态生成不同行为的函数。
python 复制代码
def power_factory(exponent):
    def power(base):
        return base ​** exponent  # 捕获exponent
    return power

square = power_factory(2)
print(square(3))  # 9(3^2)
  • 延迟绑定问题与解决
python 复制代码
# 错误示例:所有闭包共享循环变量i的最终值
def create_buttons():
    buttons = []
    for i in range(3):
        def button():
            print(f"Button {i} clicked")
        buttons.append(button)
    return buttons

# 解决:通过默认参数或嵌套函数绑定当前值
def create_buttons_fixed():
    buttons = []
    for i in range(3):
        def wrapper(x):  # 捕获当前x的值
            def button():
                print(f"Button {x} clicked")
            return button
        buttons.append(wrapper(i))
    return buttons
1. ​状态保持计数器
python 复制代码
def counter(start=0):
    count = start  # 闭包保存状态
    def increment():
        nonlocal count  # 声明为非局部变量
        count += 1
        return count
    return increment

c = counter(10)
print(c())  # 11
print(c())  # 12(闭包记住count的值)
2. ​动态配置函数
python 复制代码
def configure_printer(prefix):
    def printer(message):
        print(f"[{prefix}] {message}")  # 闭包捕获prefix
    return printer

error_log = configure_printer("ERROR")
info_log = configure_printer("INFO")

error_log("File not found!")  # [ERROR] File not found!
info_log("Process started.")  # [INFO] Process started.
3. ​缓存机制
python 复制代码
def cached(func):
    cache = {}  # 闭包保存缓存数据
    def wrapper(*args):
        if args not in cache:
            cache[args] = func(*args)
        return cache[args]
    return wrapper

@cached
def factorial(n):
    return 1 if n <= 1 else n * factorial(n-1)

print(factorial(5))  # 120(后续调用直接返回缓存结果)

二、装饰器(Decorator)

  1. 定义

装饰器是 ​一个修改其他函数行为的函数,通过接受目标函数作为参数,返回增强后的新函数,而无需修改原函数代码。

  1. 核心机制
  • 高阶函数:装饰器本身是一个返回函数的函数。

  • 语法糖 :使用 @decorator 简化装饰器调用。

  1. 基本示例
python 复制代码
def logger(func):
    def wrapper(*args, ​**kwargs):
        print(f"调用函数: {func.__name__}")
        return func(*args, ​**kwargs)
    return wrapper

@logger
def add(a, b):
    return a + b

print(add(3, 5))  
# 输出:
# 调用函数: add
# 8
  1. 带参数的装饰器

需三层嵌套函数:

python 复制代码
def repeat(n):
    def decorator(func):
        def wrapper(*args, ​**kwargs):
            result = None
            for _ in range(n):
                result = func(*args, ​**kwargs)
            return result
        return wrapper
    return decorator

@repeat(3)
def greet(name):
    print(f"Hello, {name}!")

greet("Alice")
# 输出:
# Hello, Alice!
# Hello, Alice!
# Hello, Alice!
  1. 类装饰器

通过实现 __call__ 方法:

python 复制代码
class Timer:
    def __init__(self, func):
        self.func = func

    def __call__(self, *args, ​**kwargs):
        import time
        start = time.time()
        result = self.func(*args, ​**kwargs)
        end = time.time()
        print(f"{self.func.__name__} 执行时间: {end - start:.2f}s")
        return result

@Timer
def slow_func():
    time.sleep(1)

slow_func()  # 输出:slow_func 执行时间: 1.00s
  1. 实际应用
  • 缓存(Memoization)​
python 复制代码
def memoize(func):
    cache = {}
    def wrapper(*args):
        if args not in cache:
            cache[args] = func(*args)
        return cache[args]
    return wrapper

@memoize
def fibonacci(n):
    return n if n <= 1 else fibonacci(n-1) + fibonacci(n-2)
  • 权限验证
python 复制代码
def login_required(func):
    def wrapper(user, *args, ​**kwargs):
        if user.is_authenticated:
            return func(user, *args, ​**kwargs)
        else:
            raise PermissionError("用户未登录")
    return wrapper
  1. 注意事项
  • 多个装饰器的顺序:装饰器从下往上应用,但执行顺序从上到下。

    python 复制代码
    @decorator1
    @decorator2
    def func(): pass
    # 等效于 func = decorator1(decorator2(func))
  • 保留原函数元信息 :使用 functools.wraps 保留原函数名和文档。

    python 复制代码
    from functools import wraps
    def logger(func):
        @wraps(func)
        def wrapper(*args, ​**kwargs):
            print(f"调用函数: {func.__name__}")
            return func(*args, ​**kwargs)
        return wrapper

三、闭包与装饰器的关系

  • 装饰器依赖闭包:装饰器通常通过闭包保存目标函数和装饰器参数。

  • 闭包是装饰器的基础:装饰器返回的增强函数本质上是一个闭包,捕获了原函数和装饰逻辑。


四、总结

概念 特点 应用场景
闭包 内部函数捕获外部变量,保留状态 工厂函数、延迟计算、回调
装饰器 动态扩展函数功能,不修改原代码,支持多层装饰 日志、缓存、权限、性能测试

7. Lambda 函数

匿名函数,适用于简单操作。

python 复制代码
square = lambda x: x ​** 2
print(square(4))  # 输出:16

# 结合map使用
numbers = [1, 2, 3]
squared = list(map(lambda x: x**2, numbers))  # [1, 4, 9]

8. 生成器函数

使用 yield 逐个返回值,节省内存。

python 复制代码
def count_up_to(max):
    count = 1
    while count <= max:
        yield count
        count += 1

for num in count_up_to(3):
    print(num)  # 输出:1 2 3

9. 递归函数

函数调用自身,需设置终止条件。

python 复制代码
def factorial(n):
    if n == 1:
        return 1
    else:
        return n * factorial(n-1)

print(factorial(5))  # 输出:120

10. 参数传递的注意事项

  • 可变对象(如列表)作为参数时,函数内修改会影响原始对象。
python 复制代码
def append_item(lst, item):
    lst.append(item)

my_list = [1, 2]
append_item(my_list, 3)
print(my_list)  # 输出:[1, 2, 3]

11. 函数注解

提供类型提示(Python 3+)。

复制代码
def greet(name: str, age: int) -> str:
    return f"{name} is {age} years old."
相关推荐
南玖yy38 分钟前
数据结构完全指南:C语言实现与核心原理剖析
c语言·开发语言·数据结构
Python数据分析与机器学习42 分钟前
《基于大数据的营养果蔬推荐系统的设计与实现》开题报告
大数据·开发语言·人工智能·深度学习·神经网络·算法·计算机视觉
烂蜻蜓42 分钟前
HTML 表格的详细介绍与应用
开发语言·前端·css·html·html5
爱康代码2 小时前
c语言经典基础编程题
c语言·开发语言
小杜不吃糖2 小时前
llama源码学习·model.py[1]RMSNorm归一化
python·llama
qq_白羊座3 小时前
UI自动化:poium测试库使用文档
python·selenium·ui·appium
Java开发追求者3 小时前
java 手搓一个http工具类请求传body
java·开发语言·http·调用第三方接口工具
坐吃山猪3 小时前
AutoGen多角色、多用户、多智能体对话系统
python·autogen·chainlit·deepseek
鲤籽鲲3 小时前
C# 事件使用详解
开发语言·c#·c# 知识捡漏
小白学大数据3 小时前
Python爬虫:从人民网提取视频链接的完整指南
大数据·开发语言·爬虫·python·音视频