Python 深度探讨 *args

news2024/11/13 10:29:00

点击下方卡片,关注“小白玩转Python”公众号

作为Python中最独特的语法之一,*args 在编程过程中给我们带来了很多灵活性和便利性。我认为它们反映了“Pythonic”和“Python之禅”。然而,我发现它们对于学习者(尤其是初学者)来说很难理解。在本文中,我将尽力解释Python中这个标志性的概念,并基于我的知识提供实际用例。我希望这能帮助更好地理解它。

1. “*args” 究竟是什么?

*args 代表“参数”。它允许我们向函数传递任意数量的位置参数(稍后会解释)。在函数内部,我们可以获得所有位置参数的元组。因此,我们可以在函数中对参数元组进行任何操作。下面是一个 *args 的简单示例。

def add_up(*numbers):
    result = 0
    for num in numbers:
        result += num
    return result
print(add_up(1, 2, 3))

当我们调用这个 add_up() 函数时,我们向它传递了三个位置参数。在Python中,如果我们不指定参数的名称,它们将被视为位置参数。这些参数根据它们的位置确定,因此称为位置参数。

在上面的示例中,所有位置参数 1、2、3 都传递到了函数中,并被 *numbers 参数“捕获”。然后,我们可以从这个参数 numbers 中访问所有这些参数。星号 * 告诉Python这是一个 *args 类型的参数。之后,一个简单的 for 循环将所有参数相加并打印结果。

94f8d6f70f489aa3c4beb544586f7907.jpeg

正如上面提到的,*args 的美妙之处在于它可以接受任意数量的位置参数。因此,如果需要,我们可以传递更多的参数。

print(add_up(1, 2, 3, 4))

25262d2419af5302dc2ba6e1325f621c.jpeg

在这里,我们可以通过向原始函数添加一行来验证变量 numbers 是否是一个元组。

def add_up(*numbers):
    print(type(numbers))
    result = 0
    for num in numbers:
        result += num
    return result
print(add_up(1, 2, 3))

415b0bcab3accb28bc6e338e6ad4e2a3.jpeg

Python使用元组来包含这些参数的原因主要是因为它们是不可变的。因此,在创建后它们不能被修改。

2. *args 的实际用例

现在,让我们看看 *args 的实际用例。由于它允许传递任意数量的参数,在许多情况下我们不知道需要向函数传递多少个参数,这将是它最好的使用场景。

2.1 生成动态SQL查询

其中一个常见用例是生成SQL查询。假设我们需要编写一个函数来生成带有未知数量过滤条件的SELECT语句。在大多数其他编程语言中有两个痛点。

  1. 我们需要构建一个集合类型的变量,比如数组,来打包所有的条件。然后,我们需要在函数内部解包所有的条件。

  2. 我们不知道条件的数量。它可能是零。我们还需要处理条件是否应该从“WHERE”或“AND”开始。一些开发人员喜欢在查询中添加“WHERE 1=1”,这样所有条件都可以从AND开始。

这两个痛点在Python中都可以优雅地解决。看一下下面的代码。

# Generating Dynamic SQL Queries
def create_query(table, *conditions):
    sql = f"SELECT * \nFROM {table}"
    if conditions:
        return sql + "\nWHERE " + "\nAND ".join(conditions)
    return sql

*conditions 是一个 *args 参数,可以接受零个或多个条件。该函数首先构建 SELECT 查询,然后检查 conditions 中是否有任何参数。如果有,就使用 .join() 函数构建条件子句。让我们看一些结果。从零条件开始。

# 没有条件
print(create_query("Users"))

a0ddcc90895dda63beea6363c05f74f3.jpeg

如果只有一个条件,“\nAND ”.join(conditions) 将是元组中唯一的条件。由于它只是一个连接器,所以“AND”不会出现。

# 有一个条件
print(
create_query("Users",
"age > 18"
))

e2e1f01f148df1372a932512fa3cfda5.jpeg

如果有多个条件,也可以工作。在每两个条件字符串之间,将使用“AND”作为连接器。

# 有多个条件
print(
create_query("Users",
"age > 18",
"status = 'active'",
"is_vip = 'true'"
))

0aaa88659045f894ffe671e10b0661e7.jpeg

顺便说一句,如果您想要优雅地构建一些非常复杂的SQL查询,可能有比玩弄字符串更好的做法。查看这篇文章,了解有关名为 sqlglot 的工具的更多信息。

2.2 灵活的日志消息

假设我们正在开发一个需要记录各种不同类型消息的软件。问题在于,这些不同类型的消息具有不同的组件。例如,用户登录消息只需要告诉活动类型和用户登录了谁。另一方面,文件上传日志消息具有更多的组件,如文件名、大小和经过的时间。

当然,我们可以在没有 *args 的情况下实现这一点。但是,我们要么需要在将所有组件传递给 log_messages() 函数之前构建一个列表,要么在将它们传递给函数之前将组件连接在一起作为单个字符串。有了 *args,log_messages() 函数实际上并不关心有多少个组件。

from datetime import datetime


def log_messages(*msg):
    timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    full_message = " | ".join(msg)
    print(f"[{timestamp}] {full_message}")


# Usage examples
log_messages("User logged in", "Username: Chris")
log_messages("File uploading", "Filename: report.pdf", "/ctao/document/report.pdf")
log_messages("File uploaded", "Filename: report.pdf", "Size: 2MB", "Elapsed Time: 1.3s")

在上面的代码中,我们实现了 log_messages() 函数。首先获取当前时间戳。然后,所有组件字符串都使用分隔符连接在一起,以提高可读性。最后,打印日志。示例用法仅供演示目的。在实践中,这些更可能是变量。结果看起来很棒:

5c074aad473bdb04e2a84d0fbe737127.jpeg


2.3 集合上的计算

有时,我们需要对一些集合类型(如列表和集合)进行一些计算。在这种情况下,如果我们想将几个列表放入单个列表中,这肯定可以工作,但在可读性和灵活性方面并不理想。在这种情况下,*args 将会很有帮助。

def find_common_elements(*datasets):
    # Initialize the common elements set with the first dataset
    common_elements = datasets[0] if datasets else {}


    # Intersect with the remaining datasets
    for dataset in datasets[1:]:
        common_elements.intersection_update(dataset)


    return common_elements


# Usage examples:
dataset1 = {1, 2, 3, 4}
dataset2 = {2, 3, 4, 5}
dataset3 = {3, 4, 5, 6}


common_elements = find_common_elements(dataset1, dataset2, dataset3)
print(f"The common elements in the datasets are: {common_elements}")

在上面的代码中,find_common_elements() 函数接受任意数量的集合,并获取它们的交集。它使用第一个集合来初始化公共集合。然后,使用公共集合与其他集合进行交集运算。结果如下。

ed431cdd41898240cd97cccf260935af.jpeg


3. 一些 *args 的 Python 原生用法

作为最独特的语法之一,*args 不足为奇地在许多 Python 内置模块及其函数中使用。以下是一些示例。让我们看看这些原生示例以及为什么在这些场景中使用 *args。这些是非常好的参考,可以用来指导我们的编码。会不会得到比 Python 本身更好的 Python 教程。

3.1 路径拼接

我首先想到的是 os.path.join() 方法。当我们处理文件系统时,这是最常用的函数之一。例如,如果我们想将所有这些组件连接在一起并构建文件路径,os.path.join() 将是最简单的方法。

import os
# 接受任意数量的路径组件
path = os.path.join("Users", "CTao", "Documents", "Work", "report.txt")
print(path)

以下是结果:

bd7f62ea7593fdc55f3abb4d0a3a2288.jpeg

在这个函数中,利用 *args 提供了最大的灵活性,因为我们不需要担心路径组件的数量。它还提高了代码的可读性,因为我们不需要将这些组件放入任何集合类型的变量中。

3.2 最大值和最小值

我想到的最简单的示例就是 min() 和 max() 函数。

print(max(1, 2, 3, 4, 5))
print(min(1, 2, 3, 4, 5))

6c99a81b1ce7e9e28a1172d3a80e58f9.jpeg

它们分别从 *args 中获取最大值和最小值,我们不需要传递一个列表给它。顺便说一句,我们可能会发现 max() 和 min() 实际上也支持可迭代的参数。看下面的示例。

max([1, 2, 3, 4, 5])

157b9c1a469527864ab2ec1495d06cc9.jpeg

多么灵活!它考虑了两种情况,并支持了使用该函数的两种直观方式。如果想在代码中做类似的事情,一个简单的想法是检查 args[0]。如果它是一个列表,就使用该列表。否则,使用整个 args 并迭代它。

3.3 打印函数

我相信大多数人都知道 print() 函数也支持 *args 模式。当我们使用此模式时,字符串的默认分隔符将是空格。

print("Towards", "Data", "Science")
print("Towards", "Data", "Science", sep=" | ")

cc4bef109a8c92671cb6cb0916641ad2.jpeg

在第二个示例中,sep=" | " 帮助我们自定义了分隔符。这表明我们可以将 *arg 与其他关键字参数一起使用。

4. 不要为所有事情都使用 *args!

当然,我写这篇文章是为了鼓励使用 *args。但是,请不要误解我的意思。我并不是说应该一直使用它。现在,让我们展示一个使用 *args 的不好的伪代码示例。

def my_function(*args):
    result = args[1] + args[2]
    if result > 100:
        result += args[4]
    if args[5] != args[6]:
        return result + args[7]
    else:
        return result

在这个例子中,尽管总共有 8 个参数,但它实际上通过使用 *args 损害了可读性。在这种情况下,函数严格依赖于参数的位置。最好使用命名参数或带有特定键的字典。这将在理解该函数的逻辑方面提高可读性。

总结

在本文中,我们深入探讨了 Python 中最具标志性的语法之一 — *args。它在开发函数和后续使用函数的用户方面提供了很大的灵活性。我希望现在我们对它有了更多的了解。

除此之外,我还提供了一些 *args 的实际和典型用法。可能会有更多更好的场景,但本文中的示例来自我的日常使用。欢迎评论本文,为下一位读者提供更多优秀的示例。

最后,我还列出了一些使用 *args 技巧的 Python 原生示例。这些是很好地展示了实际用例的“官方”示例。

·  END  ·

HAPPY LIFE

e2f08c13d38fdbf487c1a54782e0416b.png

本文仅供学习交流使用,如有侵权请联系作者删除

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1808758.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

DeepSpeed Learning Rate Scheduler

Learning Rate Range Test (LRRT) 训练试跑,该lr scheduler从小到大增长lr,同时记录下validatin loss;人来观察在训练多少step之后,loss崩掉(diverge)了,进而为真正跑训练,挑选合适的lr区间&…

一、Electron 环境初步搭建

新建一个文件夹,然后进行 npm init -y 进行初始化,然后我们在进行 npm i electron --save-dev , 此时我们按照官网的教程进行一个初步的搭建, 1.在 package.json 文件进行修改 {"name": "electron-ui","version…

嵌入式应用之FIFO模块原理与实现

FIFO介绍与原理 FIFO是First-In First-Out的缩写,它是一个具有先入先出特点的缓冲区。FIFO在嵌入式应用的非常广泛,可以说有数据收发的地方,基本就有FIFO的存在。或者为了降低CPU负担,提高数据处理效率,可以在积累到一…

使用 Scapy 库编写 TCP FIN 洪水攻击脚本

一、介绍 TCP FIN洪水攻击是一种分布式拒绝服务攻击(DDoS),攻击者通过向目标服务器发送大量伪造的TCP FIN(终止)数据包,使目标服务器不堪重负,无法正常处理合法请求。FIN包通常用于关闭一个TCP…

电路笔记 : 嘉立创EDA 导入、查找、设计管理器(快速寻找网络标签)功能+DRC错误检查和处理

导入功能 查找功能 可查找多种类型,如原件名称、网络标签等 设计管理器 图层查看 DRC错误 规则设置 线距问题 大多数PCB制造商能够可靠地生产5 mil间距的走线和间隙。这是一个常见的标准,适合大多数消费级和工业级电子产品。在5 mil以上的间距&#xff…

操作系统复习-存储管理之虚拟内存

虚拟内存概述 有些进程实际需要的内存很大,超过物理内存的容量。多道程序设计,使得每个进程可用物理内存更加稀缺。不可能无限增加物理内存,物理内存总有不够的时候。虚拟内存是操作系统内存管理的关键技术。使得多道程序运行和大程序运行称…

Collections工具类及其案例

package exercise;public class Demo1 {public static void main(String[] args) {//可变参数//方法形参的个数是可以发生变化的//格式:属性类型...名字//int...argsint sum getSum(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);System.out.println(sum);}//底层:可…

6.9总结

Vue生命周期 生命周期:指一个对象从创建到销毁的整个过程生命周期的八个阶段:每触发一个生命周期事件,会自动执行一个生命周期的方法(钩子) mounted:挂载完成,Vue初始化成功,HTML渲…

简记:为Docker配置服务代理

简记 为Docker配置服务代理 - 文章信息 - Author: 李俊才 (jcLee95) Visit me at CSDN: https://jclee95.blog.csdn.netMy WebSite:http://thispage.tech/Email: 291148484163.com. Shenzhen ChinaAddress of this article:https://blog.csdn.net/qq_28550263/art…

设置路径别名

一、描述 如果想要给路径设置为别名,就是常见的有些项目前面的引入文件通过开头的,也就是替换了一些固定的文件路径,怎么配置。 二、配置 import { defineConfig } from vite import react from vitejs/plugin-react import path from path…

GitHub工程获取第三方PR操作

GitHub工程获取第三方PR操作 1. 源由2. 获取第三方PRStep 1:安装ghStep 2:获取个人TokenStep 3:通过git协议获取代码Step 4:获取第三方PR分支 3. 总结 1. 源由 通常来说,GitHub上通常有三种场景: 工程管理…

MySQLWorkbench导出sql文件

MySQLWorkbench导出sql文件 前言效果图导出操作选择要导出的数据库遇到的问题解决问题查看mysql路径前言 在完成数据库搭建之后,需要为上线做准备,那么就需要导出数据库的建库sql了 本篇文章讲解的是mysql Workbench 导出数据建库脚本 效果图 导出操作 选择要导出的数据库…

什么是智慧零售?智慧零售的发展前景如何?

在零售业的快速发展中,市场竞争日益激烈,产品同质化严重,线下销售与线上商店的竞争加剧,资金成本问题日益凸显。这些问题不仅限制了零售业的发展,也给消费者带来了诸多不便。然而,智慧零售的出现&#xff0…

Java | Leetcode Java题解之第135题分发糖果

题目&#xff1a; 题解&#xff1a; class Solution {public int candy(int[] ratings) {int n ratings.length;int ret 1;int inc 1, dec 0, pre 1;for (int i 1; i < n; i) {if (ratings[i] > ratings[i - 1]) {dec 0;pre ratings[i] ratings[i - 1] ? 1 : …

互联网应用主流框架整合之SpringMVC初始化及各组件工作原理

Spring MVC的初始化和流程 MVC理念的发展 SpringMVC是Spring提供给Web应用领域的框架设计&#xff0c;MVC分别是Model-View-Controller的缩写&#xff0c;它是一个设计理念&#xff0c;不仅仅存在于Java中&#xff0c;各类语言及开发均可用&#xff0c;其运转流程和各组件的应…

如何使用照相机

前言&#xff1a; ” 接上篇&#xff0c;https://t.zsxq.com/19UoFe33k&#xff0c;本文是整理的关于《美国纽约摄影学院 摄影教材》这本书&#xff0c;第一单元 - 第3课 - 如何使用照相机&#xff0c;课后习题及解答。“ 1、对于一架典型的单镜头反光照相机&#xff0c;取景时…

并查集-求有向图中是否有环

目录 一、问题描述 二、解题思路 初始化 遍历边并尝试合并 结果分析 三、代码实现 四、刷题链接 一、问题描述 二、解题思路 题目的目的就是检查有向图中是否存在环&#xff0c;这里提供两种判断方式&#xff1a; 1.使用并查集 2.使用拓扑排序 我们在这里先只给出并查…

【C语言】宏详解(下卷)

前言 紧接上卷&#xff0c;我们继续来了解宏。 宏替换的规则 1.在调用宏时&#xff0c;首先对参数进行检查&#xff0c;看看是否包含任何由#define定义的符号。如果是&#xff0c;它们首先被替换。 2.替换文本随后被插入到程序中原来文本的位置。对于宏&#xff0c;参数名被他…

React保姆级教学

React保姆级教学 一、创建第一个react项目二、JSX基本语法与react基础知识1、 插值语法&#xff1a;2、 循环一个简单列表3、 实现简单条件渲染4、 实现复杂的条件渲染5、 事件绑定6、 基础组件&#xff08;函数组件&#xff09;7、 使用useState8、 基础样式控制9、 动态类名1…

[FreeRTOS 基础知识] 保存现场与恢复现场

文章目录 什么是现场&#xff1f;保存现场的数据存放在哪里&#xff1f;保护现场的场景 什么是现场&#xff1f; 在[FreeRTOS 基础知识] 栈 与 汇编语言文章中解析了fun_c汇编函数&#xff0c;假设在执行fun_c函数的过程中产生高优先级的中断。如下图所示。 此时刚从RAM的SP栈…