* 和 **

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 如何使代码更加通用和可重用。在实际开发中,类似的技巧可以用于创建中间件、插件系统、事件处理器等需要高度灵活性和可扩展性的场景。

相关推荐
思则变2 小时前
[Pytest] [Part 2]增加 log功能
开发语言·python·pytest
漫谈网络2 小时前
WebSocket 在前后端的完整使用流程
javascript·python·websocket
try2find3 小时前
安装llama-cpp-python踩坑记
开发语言·python·llama
博观而约取5 小时前
Django ORM 1. 创建模型(Model)
数据库·python·django
精灵vector6 小时前
构建专家级SQL Agent交互
python·aigc·ai编程
Zonda要好好学习6 小时前
Python入门Day2
开发语言·python
Vertira6 小时前
pdf 合并 python实现(已解决)
前端·python·pdf
太凉6 小时前
Python之 sorted() 函数的基本语法
python
项目題供诗7 小时前
黑马python(二十四)
开发语言·python
晓13137 小时前
OpenCV篇——项目(二)OCR文档扫描
人工智能·python·opencv·pycharm·ocr