* 和 **

1. *args(可变位置参数)

*args用于在函数定义中接收任意数量的位置参数。这些参数在函数内部被当作一个元组来处理。

python 复制代码
def sum_numbers(*args):
    return sum(args)

print(sum_numbers(1, 2, 3))  # 输出:6
print(sum_numbers(4, 5, 6, 7))  # 输出:22

在这个例子中,sum_numbers函数可以接收任意数量的数字,并计算它们的和。

2. **kwargs(可变关键字参数)

**kwargs用于在函数定义中接收任意数量的关键字参数。这些参数在函数内部被当作一个字典来处理。

python 复制代码
def print_info(**kwargs):
    for key, value in kwargs.items():
        print(f"{key}: {value}")

print_info(name="张三", age=30)
# 输出:
# name: 张三
# age: 30

在这个例子中,print_info函数可以接收任意数量的关键字参数,并打印它们。

3. *** 在函数调用时的作用

*** 在函数调用时展开列表和字典的内容作为位置参数和关键字参数,下面是一个简单的例子:

python 复制代码
def my_function(a, b, c, d=None, e=None):
    print(a, b, c, d, e)

# 定义一个列表,包含将要传递给函数的位置参数
ls = [1, 2, 3]

# 定义一个字典,包含将要传递给函数的关键字参数
kwargs = {'d': 4, 'e': 5}

# 使用 * 展开列表为位置参数,** 展开字典为关键字参数
my_function(*ls, **kwargs)

在这个例子中,my_function 需要至少三个位置参数 a, b, c,以及两个可选的关键字参数 de。我们创建了一个列表 ls,它包含了要传递给函数的前三个位置参数的值。我们还创建了一个字典 kwargs,它包含了要传递给函数的关键字参数的值。

当我们调用 my_function(*ls, **kwargs) 时,*ls 会将列表 ls 展开为位置参数,即 1, 2, 3,而 **kwargs 会将字典 kwargs 展开为关键字参数,即 d=4, e=5。因此,函数调用等同于 my_function(1, 2, 3, d=4, e=5)

输出将会是:

1 2 3 4 5

这种方式在你不确定函数需要多少参数,或者你想在运行时动态地构建参数列表时特别有用。

4. 重中之重

python 复制代码
# 定义函数 func1,接受三个参数:x, y, z
def func1(x, y, z):
    # 打印出一段包含字符串 'func1>>>' 和这三个参数的信息
    print('func1>>>', x, y, z)

# 定义函数 func2,它使用 *args 和 **kwargs 来接受任意数量的位置参数和关键字参数
def func2(*args, **kwargs):
    # 调用 func1,并使用 * 和 ** 来展开 args 和 kwargs 中的参数
    # 这样 func1 就能接收到传递给 func2 的位置参数和关键字参数了
    func1(*args, **kwargs)

# 调用 func2 函数,传入两个位置参数 1 和 2,以及一个关键字参数 z=3
# 这个调用等同于 func1(1, 2, 3),因为位置参数和关键字参数都被正确传递了
func2(1, 2, z=3)

func2(1, 2, z=3) 被调用时:

  • *args 捕获了位置参数 12
  • **kwargs 捕获了关键字参数 z=3
  • 然后 func2 内部调用了 func1,并且通过 *args**kwargs 展开了参数。
  • 因此 func1 接收到了正确的参数值 x=1, y=2, z=3 并执行了打印语句。

在这个例子中,func2 的定义展示了 Python 中参数传递的灵活性和强大功能。通过使用 *args**kwargsfunc2 可以接受任意数量和类型的参数,并将它们正确地传递给 func1。这种技术有几个重要的应用场景,使其成为编程中的"重中之重":

  1. 代码解耦func2 不需要知道 func1 的确切参数列表。这意味着如果 func1 的签名改变了,只要新的签名仍然兼容传递的参数,func2 就不需要修改。这有助于减少代码之间的耦合,使代码更容易维护和扩展。

  2. 动态调用 :在某些情况下,你可能不知道在编写 func2 时会调用哪个函数(在这个例子中是 func1)。通过使用 *args**kwargsfunc2 可以动态地将参数传递给在运行时确定的任何函数。

  3. 参数转发 :在复杂的函数调用链中,一个函数可能需要将接收到的参数原封不动地传递给另一个函数。*args**kwargs 提供了一种简洁而有效的方式来转发这些参数,而无需显式地列出它们。

  4. API 设计 :当设计库或框架时,使用 *args**kwargs 可以让你的 API 更加灵活和可扩展。用户可以传递他们需要的任何参数,而你的代码只需要关心处理这些参数的核心逻辑。

  5. 可扩展性 :在软件开发中,需求经常变化。使用 *args**kwargs 编写的函数更容易适应这些变化,因为它们可以接受新类型的参数而无需修改函数签名。

在这个例子中,func2(1, 2, z=3) 调用展示了如何同时使用位置参数(通过 *args 传递)和关键字参数(通过 **kwargs 传递)。这种混合使用非常常见,并且在处理复杂的函数调用和参数传递时非常有用。因此,理解并能够熟练运用这些技术对于成为一名高效的 Python 程序员来说是非常重要的。

实用的案例

装饰器

*args**kwargs 在许多实际场景中都非常有用。下面是一个复杂的例子,它结合了 *args**kwargs 来构建一个灵活的函数装饰器,该装饰器可以用于记录函数调用的信息。

首先,我们来定义一个装饰器 log_function_call,它使用 *args**kwargs 来接收任意数量和类型的参数,并记录函数调用的详细信息:

python 复制代码
import functools
import datetime

def log_function_call(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        # 记录函数调用前的时间
        start_time = datetime.datetime.now()
        
        # 调用原始函数并记录返回值
        result = func(*args, **kwargs)
        
        # 记录函数调用后的时间
        end_time = datetime.datetime.now()
        
        # 获取函数参数名称和值
        arg_names = func.__code__.co_varnames[:func.__code__.co_argcount]
        arg_values = [repr(args[i]) for i in range(len(args))]
        arg_values.extend(f"{k}={repr(v)}" for k, v in kwargs.items())
        full_args = ", ".join(arg_values)
        if arg_names:
            full_args = f"({', '.join(arg_names)}) = ({full_args})"
        
        # 输出日志信息
        print(f"Function {func.__name__} {full_args} called at {start_time} and returned at {end_time}")
        print(f"Execution time: {end_time - start_time}")
        print(f"Result: {result}\n")
        
        return result
    
    return wrapper

现在,让我们使用这个装饰器来装饰一个简单的函数,并查看它的效果:

python 复制代码
@log_function_call
def add(x, y, z=10):
    return x + y + z

# 函数调用
print(add(1, 2))          # 位置参数
print(add(1, 2, z=20))    # 位置参数和关键字参数
print(add(y=2, x=1))      # 关键字参数

当你运行这段代码时,你会看到每个函数调用之前和之后的详细日志信息,包括函数名称、参数值、调用时间和执行时间。这个装饰器利用了 *args**kwargs 的灵活性,使得它可以无需修改就能用于具有不同参数签名的多个函数。

python 复制代码
Function add (x, y, z) = (1, 2) called at 2024-03-26 10:54:07.619490 and returned at 2024-03-26 10:54:07.619490
Execution time: 0:00:00
Result: 13

13
Function add (x, y, z) = (1, 2, z=20) called at 2024-03-26 10:54:07.619490 and returned at 2024-03-26 10:54:07.619490
Execution time: 0:00:00
Result: 23

23
Function add (x, y, z) = (y=2, x=1) called at 2024-03-26 10:54:07.619490 and returned at 2024-03-26 10:54:07.619490
Execution time: 0:00:00
Result: 13

13

这个例子展示了 *args**kwargs 如何使代码更加通用和可重用。在实际开发中,类似的技巧可以用于创建中间件、插件系统、事件处理器等需要高度灵活性和可扩展性的场景。

相关推荐
waterHBO1 小时前
python 爬虫 selenium 笔记
爬虫·python·selenium
编程零零七2 小时前
Python数据分析工具(三):pymssql的用法
开发语言·前端·数据库·python·oracle·数据分析·pymssql
AIAdvocate4 小时前
Pandas_数据结构详解
数据结构·python·pandas
小言从不摸鱼4 小时前
【AI大模型】ChatGPT模型原理介绍(下)
人工智能·python·深度学习·机器学习·自然语言处理·chatgpt
FreakStudio6 小时前
全网最适合入门的面向对象编程教程:50 Python函数方法与接口-接口和抽象基类
python·嵌入式·面向对象·电子diy
redcocal7 小时前
地平线秋招
python·嵌入式硬件·算法·fpga开发·求职招聘
artificiali8 小时前
Anaconda配置pytorch的基本操作
人工智能·pytorch·python
RaidenQ8 小时前
2024.9.13 Python与图像处理新国大EE5731课程大作业,索贝尔算子计算边缘,高斯核模糊边缘,Haar小波计算边缘
图像处理·python·算法·课程设计
花生了什么树~.8 小时前
python基础知识(六)--字典遍历、公共运算符、公共方法、函数、变量分类、参数分类、拆包、引用
开发语言·python
Trouvaille ~9 小时前
【Python篇】深度探索NumPy(下篇):从科学计算到机器学习的高效实战技巧
图像处理·python·机器学习·numpy·信号处理·时间序列分析·科学计算