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."
相关推荐
Jay_273 分钟前
python项目如何创建docker环境
开发语言·python·docker
xlsw_10 分钟前
MyBatis之测试添加功能
java·开发语言·mybatis
忘梓.19 分钟前
从二叉树到 STL:揭开 set 容器的本质与用法
开发语言·c++
老胖闲聊22 分钟前
Python Django完整教程与代码示例
数据库·python·django
爬虫程序猿26 分钟前
利用 Python 爬虫获取淘宝商品详情
开发语言·爬虫·python
noravinsc26 分钟前
django paramiko 跳转登录
后端·python·django
声声codeGrandMaster28 分钟前
Django之表格上传
后端·python·django
曹勖之42 分钟前
在MATLAB中使用自定义的ROS2消息
开发语言·matlab·机器人·ros·simulink·ros2
元直数字电路验证1 小时前
Python数据分析及可视化中常用的6个库及函数(一)
python·numpy
waterHBO1 小时前
一个小小的 flask app, 几个小工具,拼凑一下
javascript·vscode·python·flask·web app·agent mode·vibe coding