原文:
towardsdatascience.com/do-you-really-know-args-in-python-1e3402c77190
图片由Miguel Á. Padriñán来自Pixabay提供
作为 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 循环将所有参数相加并打印结果。
如上所述,*arg的美丽之处在于它可以接受任意数量的位置参数。因此,如果我们需要的话,我们可以传递更多的参数。
print(add_up(1, 2, 3, 4))
在这里,我们可以通过向原始函数添加一行代码来验证变量numbers是否是一个元组。
def add_up(*numbers):
print(type(numbers))
result = 0
for num in numbers:
result += num
return result
print(add_up(1, 2, 3))
Python 使用元组来包含这些参数的原因主要是由于其不可变性。因此,它们在创建后不能被修改。
2. *args的实际用例
图片由 Rudy 和 Peter Skitterians 提供,来自 Pixabay
现在,让我们看看 *args 的实际用例。由于它允许传递任意数量的参数,在许多场景中,我们不知道需要传递多少参数给函数,这将使其成为最佳使用场景。
2.1 生成动态 SQL 查询
其中一个常见的用例是生成 SQL 查询。
假设我们需要编写一个函数来生成具有未知数量过滤条件的 SELECT 语句。在大多数其他编程语言中,有两个痛点。
-
我们需要构建一个收集类型变量,如数组,来打包所有条件。然后,我们需要在函数内部解包所有条件。
-
我们不知道条件数量的多少。它可能是零。我们还需要处理条件是否应该从 “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() 函数构建条件子句。
让我们看看一些结果。从零条件开始。
# Without condition
print(create_query("Users"))
如果只有一个条件,"nAND ".join(conditions) 将给出元组中唯一的条件。由于它只是一个连接符,所以 “AND” 不会出现。
# With one condition
print(
create_query("Users",
"age > 18"
))
如果我们有多个条件,它也会工作。这里将 “AND” 用作两个条件字符串之间的连接符。
# With multiple conditions
print(
create_query("Users",
"age > 18",
"status = 'active'",
"is_vip = 'true'"
))
多么优雅的解决方案!
BTW,如果你想优雅地构建一些复杂的 SQL 查询,可能比玩字符串操作有更好的实践。查看这篇文章了解更多关于名为 sqlglot 的工具的信息。
此外,如果你不太熟悉 <str>.join() 函数,这里有一篇文章可以直观地解释它。
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()函数。它首先获取当前时间戳。然后,所有组件字符串通过分隔符连接起来以提高可读性。最后,打印日志。
使用示例仅用于演示目的。在实践中,这些更可能是变量。
结果看起来很棒。
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. 一些 Python 原生对*args 的使用
图片由Markus Weber在Pixabay提供
作为最独特的语法之一,*args不出所料地被用在许多 Python 内置模块及其函数中。以下是一些示例。
让我们看看这些原生示例以及为什么在这些场景中使用*args。这些是非常好的参考,可以用来指导我们的编码。你不会找到比 Python 本身更好的 Python 教程了 🙂
3.1 路径连接
我首先想到的是os.path.join()方法。在我们处理文件系统时,这是最常用的函数之一。
例如,如果我们想将这些组件连接起来并构建一个文件路径,os.path.join()将是最简单的方法。
import os
# Take any number of path components
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 中获取最大和最小数字,我们不需要向它传递列表。
BTW,你可能发现 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 用于所有情况!
图片由 Free Photos 来自 Pixabay 提供
当然,我写这篇文章是为了鼓励你们使用 *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 实际上损害了可读性。在这种情况下,函数严格依赖于参数的位置。使用命名参数或具有特定键的字典会更好,这将在理解函数的确切逻辑方面提高可读性。
摘要
图片由 Leopictures 来自 Pixabay
在这篇文章中,我们深入探讨了 Python 中最具标志性的语法之一——*args。它在开发函数以及后续使用该函数的用户方面提供了极大的灵活性。我希望你现在对它有了更深的理解。
除了这些,我还提供了一些关于*args的实际和典型用法。肯定还有更多和更好的场景,但文章中的例子都来源于我的日常使用。请随意评论这篇文章,为后续读者提供更多优秀的例子。
最后,我还列出了一些利用*args技巧的 Python 原生示例。这些都是“官方”示例,非常有效地展示了实际的应用场景。
希望这篇文章能有所帮助。如果您想了解更多关于**kwargs的信息,请继续关注我,下一篇文章将会很快发布。
1463

被折叠的 条评论
为什么被折叠?



