13 KiB
Python 中什么时候用省略号?
在英语写作中,你可以用省略号来表示你漏掉了什么。本质上,您使用三个点(...)来替换内容。但是省略号不仅仅存在于散文中——你可能也在 Python 源代码中看到过三个点。
省略号文字(...)计算为 Python 的 Ellipsis 。因为EllipsisT8 是一个内置的常量,你可以使用Ellipsis或者...而不用导入它:
>>> ...
Ellipsis
>>> Ellipsis
Ellipsis
>>> ... is Ellipsis
True
虽然三个点作为 Python 语法看起来很奇怪,但是在某些情况下使用...会很方便。但是在 Python 中什么时候应该使用Ellipsis?
源代码: 点击这里下载免费的源代码,你将使用它来掌握省略号文字。
简而言之:在 Python 中使用省略号作为占位符
虽然您可以互换使用...和Ellipsis,但您通常会在代码中选择...。类似于在英语中使用三个点来省略内容,可以使用 Python 中的省略号作为未写代码的占位符:
# ellipsis_example.py
def do_nothing():
...
do_nothing()
当您运行ellipsis_example.py并执行do_nothing()时,Python 会毫无怨言地运行:
$ python ellipsis_example.py
$
当您在 Python 中执行一个函数体中只包含...的函数时,不会出现错误。这意味着您可以使用省略号作为占位符,类似于 pass关键字。
使用三个点创建最小的视觉混乱。所以,当你在线分享你的代码时,替换不相关的代码是很方便的。
省略代码的常见情况是使用存根的时候。您可以将存根视为实函数的替身。当您只需要一个函数签名但不想执行函数体中的代码时,存根就可以派上用场了。例如,在开发应用程序时,您可能希望阻止外部请求。
假设你有一个烧瓶项目,你在custom_stats.count_visitor()创建了自己的访客计数器。count_visitor()功能连接到跟踪访问者数量的数据库。为了在调试模式下测试应用程序时不把自己算进去,可以创建一个count_visitor()的存根:
1# app.py
2
3from flask import Flask
4
5from custom_stats import count_visitor
6
7app = Flask(__name__)
8
9if app.debug:
10 def count_visitor(): ... 11
12@app.route("/")
13def home():
14 count_visitor()
15 return "Hello, world!"
因为在这种情况下count_visitor()的内容无关紧要,所以在函数体中使用省略号是个好主意。当您在调试模式下运行 Flask 应用程序时,Python 调用count_visitor()没有错误或不必要的副作用:
$ flask --app app --debug run
* Serving Flask app 'app'
* Debug mode: on
如果您在调试模式下运行 Flask 应用程序,那么第 14 行中的count_visitor()引用第 10 行中的存根。在count_visitor()的函数体中使用...可以帮助你测试你的应用程序而不会有副作用。
上面的例子显示了如何在较小的范围内使用存根。对于更大的项目,存根经常用在单元测试中,因为它们有助于以一种隔离的方式测试你的部分代码。
此外,如果您熟悉 Python 中的类型检查,那么关于省略号和存根的讨论可能会让您想起一些事情。
进行类型检查最常用的工具是 mypy 。为了确定标准库和第三方库定义的类型, mypy 使用存根文件:
存根文件是一个包含 Python 模块公共接口框架的文件,包括类、变量、函数——最重要的是它们的类型。(来源)
您可以访问 Python 的类型化存储库,并探索这个存储库包含的存根文件中...的用法。当你深入到静态类型的主题时,你可能会发现Ellipsis常量的另一个用例。在下一节中,您将学习何时在类型提示中使用...。
类型提示中的省略号是什么意思?
在上一节中,您了解了可以使用Ellipsis作为存根文件的占位符,包括在类型检查时。但是你也可以在类型提示中使用...。在本节中,您将学习如何使用...来:
- 指定同质类型的可变长度元组
- 替换可调用函数的参数列表
类型提示是一种很好的方式,可以明确您在代码中期望的数据类型。但有时,您希望在不完全限制用户如何使用对象的情况下使用类型提示。例如,您可能希望指定一个只包含整数的元组,但是整数的数量可以是任意的。这时省略号就派上用场了:
1# tuple_example.py
2
3numbers: tuple[int, ...] 4
5# Allowed:
6numbers = ()
7numbers = (1,)
8numbers = (4, 5, 6, 99)
9
10# Not allowed:
11numbers = (1, "a")
12numbers = [1, 3]
在第 3 行,您定义了一个类型为元组的变量numbers。numbers变量必须是只包含整数的元组。总量不重要。
第 6、7 和 8 行中的变量定义是有效的,因为它们符合类型提示。不允许使用numbers的其他定义:
- 第 11 行不包含同质项目。
- 第 12 行不是元组,是列表。
如果您安装了 mypy,那么您可以使用 mypy 运行代码来列出这两个错误:
$ mypy tuple_example.py
tuple_example.py:11: error: Incompatible types in assignment
(expression has type "Tuple[int, str]", variable has type "Tuple[int, ...]")
tuple_example.py:12: error: Incompatible types in assignment
(expression has type "List[int]", variable has type "Tuple[int, ...]")
在 tuple 类型提示中使用...意味着您希望 tuple 中的所有项都是相同的类型。
另一方面,当您对可调用类型使用省略号文字时,您实际上解除了对如何调用可调用类型的一些限制,可能是在参数的数量或类型方面:
1from typing import Callable
2
3def add_one(i: int) -> int:
4 return i + 1
5
6def multiply_with(x: int, y: int) -> int:
7 return x * y
8
9def as_pixels(i: int) -> str:
10 return f"{i}px"
11
12def calculate(i: int, action: Callable[..., int], *args: int) -> int: 13 return action(i, *args)
14
15# Works:
16calculate(1, add_one)
17calculate(1, multiply_with, 3)
18
19# Doesn't work:
20calculate(1, 3)
21calculate(1, as_pixels)
在第 12 行,您定义了一个可调用的参数,action。这个可调用函数可以接受任意数量和类型的参数,但必须返回一个整数。有了*args: int,你还允许可变数量的可选参数,只要它们是整数。在第 13 行的calculate()函数体中,用整数i和任何其他传入的参数调用action。
当你定义一个可调用类型时,你必须让 Python 知道你允许什么类型作为输入,以及你期望这个可调用类型返回什么类型。通过使用Callable[..., int],您说您不介意这个可调用函数接受多少和哪些类型的参数。然而,您已经指定它必须返回一个整数。
您在第 16 行和第 17 行中作为参数传递给calculate()的函数符合您设置的规则。add_one()和multiply_with()都是返回整数的可调用函数。
第 20 行的代码无效,因为3是不可调用的。可调用函数必须是你可以调用的东西,因此得名。
虽然as_pixels()是可调用的,但是它在第 21 行的用法也是无效的。在第 10 行,你正在创建一个 f 弦。您得到一个字符串作为返回值,这不是您期望的整数类型。
在上面的例子中,您已经研究了如何在元组和可调用的类型提示中使用省略号文字:
| 类型 | Ellipsis用法 |
|---|---|
tuple |
用统一类型定义未知长度的数据元组 |
Callable |
代表可调用的参数列表,移除限制 |
接下来,您将学习如何在 NumPy 中使用Ellipsis。
在 NumPy 中如何使用省略号进行切片?
如果你以前和 NumPy 一起工作过,那么你可能会遇到Ellipsis的另一种用法。在 NumPy 中,您可以用省略号文本分割多维数组。
从一个没有利用 NumPy 中的Ellipsis的例子开始:
>>> import numpy as np
>>> dimensions = 3
>>> items_per_dimension = 2
>>> max_items = items_per_dimension**dimensions
>>> axes = np.repeat(items_per_dimension, dimensions)
>>> arr = np.arange(max_items).reshape(axes)
>>> arr
array([[[0, 1],
[2, 3]],
[[4, 5],
[6, 7]]])
在本例中,您将通过组合 NumPy 中的 .arange() 和 .reshape() 来创建一个三维数组。
如果您想指定最后一个维度的第一项,那么您可以借助冒号(:)用分割 NumPy 数组:
>>> arr[:, :, 0]
array([[0, 2],
[4, 6]])
因为arr有三个维度,所以需要指定三个切片。但是想象一下,如果你增加更多的维度,语法会变得多么烦人!更糟糕的是,你无法判断一个数组有多少个维度:
>>> import numpy as np
>>> dimensions = np.random.randint(1,10)
>>> items_per_dimension = 2
>>> max_items = items_per_dimension**dimensions
>>> axes = np.repeat(items_per_dimension, dimensions)
>>> arr = np.arange(max_items).reshape(axes)
在本例中,您正在创建一个最多可以有十个维度的数组。你可以使用 NumPy 的.ndim()T4 来找出arr有多少个维度。但是在这种情况下,使用...是更好的方法:
>>> arr[..., 0]
array([[[[ 0, 2],
[ 4, 6]],
[[ 8, 10],
[12, 14]]],
[[[16, 18],
[20, 22]],
[[24, 26],
[28, 30]]]])
这里,arr有五个维度。因为维度的数量是随机的,所以您的输出可能看起来不同。尽管如此,用...来指定你的多维数组还是可以的。
NumPy 提供了更多的选项来使用Ellipsis来指定一个元素或者数组的范围。查看 NumPy: Ellipsis ( ... ) for ndarray ,发现这三个小点的更多用例。
Python 中的三个点永远是省略号吗?
一旦你学习了 Python 的Ellipsis,你可能会更加注意 Python 世界中每个省略号的出现。然而,你可能会在 Python 中看到三个点,不代表Ellipsis常量。
在 Python 交互 shell 中,三个点表示二级提示:
>>> def hello_world():
... print("Hello, world!")
...
例如,当您在 Python 解释器中定义函数时,或者当您创建 for循环时,提示会持续多行。
在上面的例子中,这三个点不是省略号,而是函数体的二级提示。
在 Python 中,你还在其他地方发现过三个点吗?请在下面的评论中与真正的 Python 社区分享您的发现!
结论
省略号文字(...)计算为Ellipsis常量。最常见的是,你可以使用...作为占位符,例如当你创建函数的存根时。
在类型提示中,这三个点可以在你需要灵活性的时候派上用场。您可以指定一个同质类型的可变长度元组,并用省略号文本替换可调用类型的参数列表。
如果您使用 NumPy,那么您可以使用...通过用Ellipsis对象替换可变长度维度来简化切片语法。有了这三个点提供的整洁的语法,您可以使您的代码更具可读性。
根据经验,你可以记住你通常使用 Python 的Ellipsis来省略代码。有些人甚至会说省略号可以让不完整的代码看起来很可爱(T2)
源代码: 点击这里下载免费的源代码,你将使用它来掌握省略号文字。***