社区所有版块导航
Python
python开源   Django   Python   DjangoApp   pycharm  
DATA
docker   Elasticsearch  
aigc
aigc   chatgpt  
WEB开发
linux   MongoDB   Redis   DATABASE   NGINX   其他Web框架   web工具   zookeeper   tornado   NoSql   Bootstrap   js   peewee   Git   bottle   IE   MQ   Jquery  
机器学习
机器学习算法  
Python88.com
反馈   公告   社区推广  
产品
短视频  
印度
印度  
Py学习  »  Python

Python 深度探讨 *args

小白玩转Python • 6 月前 • 211 次点击  
点击下方卡片,关注“小白玩转Python”公众号

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

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

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

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


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

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

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

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

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

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


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


2.1 生成动态SQL查询
其中一个常见用例是生成SQL查询。假设我们需要编写一个函数来生成带有未知数量过滤条件的SELECT语句。在大多数其他编程语言中有两个痛点。
  1. 我们需要构建一个集合类型的变量,比如数组,来打包所有的条件。然后,我们需要在函数内部解包所有的条件。

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

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

# Generating Dynamic SQL Queriesdef 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"))

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


如果有多个条件,也可以工作。在每两个条件字符串之间,将使用“AND”作为连接器。
# 有多个条件print(create_query("Users","age > 18","status = 'active'","is_vip = 'true'"))

顺便说一句,如果您想要优雅地构建一些非常复杂的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 exampleslog_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() 函数。首先获取当前时间戳。然后,所有组件字符串都使用分隔符连接在一起,以提高可读性。最后,打印日志。示例用法仅供演示目的。在实践中,这些更可能是变量。结果看起来很棒:


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() 函数接受任意数量的集合,并获取它们的交集。它使用第一个集合来初始化公共集合。然后,使用公共集合与其他集合进行交集运算。结果如下。


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)

以下是结果:

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


3.2 最大值和最小值

我想到的最简单的示例就是 min() max() 函数。
print(max(1, 2, 3, 4, 5))print(min(1, 2, 3, 4, 5))

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


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


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

在第二个示例中,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

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

Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/171065
 
211 次点击