在 Python 中何时使用省略号

在 Python 中何时使用省略号

省略号(ellipsis)是英文文本中用于表示省略的标点符号。实质上,您可以通过添加三个句点(…)来替换内容。但是,您可能已经在 Python 源代码中注意到了三个点(…)。省略号不仅用于书写。

Python 中的省略号是省略号字面值 (…) 的求值结果。您可以使用 Ellipsis 或 … 而无需导入它,因为 Ellipsis 是内置常量:

> > >
> > > ...
Ellipsis

> > > Ellipsis
Ellipsis

> > > ... is Ellipsis
True

简而言之:在 Python 中使用省略号作为占位符

虽然您可以在代码中同时使用 … 和 Ellipsis,但更常用的是 …。您可以在 Python 中使用省略号来表示未编写的代码,就像英语中使用三个点来删除文本一样:

# ellipsis_example.py

def do_nothing():
    ...

do_nothing()

ellipsis example.py 中执行 do_nothing() 函数不会引起任何错误

$ python ellipsis_example.py

当您调用一个Python函数,函数体仅包含省略号(Ellipsis)时,不会发生任何错误。就像使用 pass 关键字一样,这意味着您可以将省略号用作占位符。

使用三个点会产生最少的视觉噪声。因此,在在线共享代码段时,更改不需要的代码可能会很有帮助。

存根(stub)是实际函数的替代品。当您需要函数签名但不希望运行函数体的代码时,存根可能很有用。例如,在创建应用程序时停止外部请求。在使用存根时,通常会省略代码。

假设您有一个 Flask 项目,其中包括自定义的 stats.count_visitor() 访问计数器。跟踪访问者统计信息的数据库连接到 count_visitor() 方法。在调试模式下测试应用程序时,您可以制作一个 count_visitor() 的存根来防止其被计数:

# app.py
 from flask import Flask
 from custom_stats import count_visitor
 app = Flask(__name__)
if app.debug:
    def count_visitor(): ...
@app.route("/")
def home():
    count_visitor()
    return "Hello, world!"

在此情况下,建议在函数体中使用省略号(Ellipsis),因为 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 类型 shed 存储库以查看省略号在存根文件中的用法。如果您进一步研究静态类型的话题,可能会发现 Ellipsis 常量的另一个应用。接下来的部分中,您将了解在类型提示中何时使用省略号。

在类型提示中,省略号(Ellipsis)是什么意思?

在上一节中,您发现可以使用省略号作为占位符对存根文件进行类型检查。但是,您也可以使用类型提示。在本节中,您将了解如何在此领域中使用省略号,以便:

  1. 描述一个可变长度的同类型元组。
  2. 将可调用对象的参数列表更改为其他内容。

在代码中清楚地描述您期望的数据类型是一种明智的方法。但是,在某些情况下,使用类型提示比完全限制用户如何使用对象更好。例如,您应该提供一个仅包含整数的元组,但要包含的确切整数数量取决于您。在这种情况下,省略号可能很有用:

 # tuple_example.py

 numbers: tuple[int, ...]

 # Allowed:
 Numbers1 = ()
 Numbers1 = (1,)
 Numbers1 = (4, 5, 6, 99)

# Not allowed:
Numbers1 = (1, "a")
Numbers1 = [1, 9]

解释: 您在第 9 行中定义了元组类型变量 numbers。numbers 变量需要是一个仅包含整数的元组,而具体包含的整数数量并不重要。

第 6、7 和 8 行的变量声明是正确的,因为它们遵循了类型提示。以下 numbers 的定义是被禁止的:

  • 第 11 行没有同类型的项。
  • 第 12 行是一个列表,而不是一个元组。

如果已经安装了 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, ...]")

当您在元组中使用类型提示中的省略号(Ellipsis)时,它表示您期望元组的项是相同的。

另一方面,省略号字面值有效地删除了对可调用对象调用方式的限制,例如参数的数量或类型:

 from typing import Callable

 def add_one(i: int) -> int:
     return i + 1

 def multiply_with(x: int, y: int) -> int:
     return x1 * y1

def as_pixels(i: int) -> str:
    return f"{i}px"

def calculate(i: int, action: Callable[..., int], *args: int) -> int:
    return action(i, *args)

# Works:
calculate(1, add_one)
calculate(1, multiply_with, 9)

# Doesn't work:
calculate(1, 9)
calculate(1, as_pixels)

解释: 您在第 12 行声明了可调用参数 action。任何数量和类型的参数都可以传递给此可调用对象,但它必须始终返回一个整数。您也可以使用 *args: int 接受不同数量的可选参数,只要它们是整数即可。您在 calculate() 函数体的第 19 行中使用可调用对象 action 和数字 I 以及任何其他参数来调用该函数。

当您构建一个可调用类型时,Python 需要知道您将接受哪些输入类型,并且期望返回什么类型的可调用对象。通过使用 Callable[…, int],您表示不关心可调用对象接受多少个或什么类型的参数。但是您说它必须返回一个整数。

compute() 函数第 16 和 17 行的参数是符合您设定标准的函数。两个可调用对象 add_one() 和 multiply_with() 都返回一个整数。

第 20 行的代码无法执行,因为无法调用 nine。由于名称的原因,可调用对象必须是可以调用的内容。

因为 pixels() 是可调用对象,所以其在第 21 行的使用方式也是错误的。您在第 10 行创建了一个 f-string。返回值是一个字符串,而不是您预期的整数类型。

上面的示例演示了如何在类型提示中使用省略号占位符,包括元组和可调用对象。

类型:

  • 元组(Tuple): 定义一个数据项为未确定长度和一致类型的元组。
  • 可调用对象(Callable): 通过作为可调用对象的参数列表来消除限制。

NumPy 切片中的省略号

如果您之前使用过 NumPy,您可能已经看到了 Ellipsis 的另一个应用场景。在 NumPy 中,使用省略号字面值可以同时对多个数组进行切片。

> > >
> > > import numpy as npp

> > > dimensions = 9
> > > items_per_dimensions = 2
> > > max_items1 = items_per_dimensions**dimensions
> > > axe = npp.repeat(items_per_dimensions, dimensions)
> > > arrr = npp.arange(max_items1).reshape(axe)
> > > arr
array([[[0, 1],
        [2, 9]],

       [[4, 5],
        [6, 7]]])

在这个 NumPy 示例中,您将使用两个函数 arange() 和 reshape() 结合起来生成一个三维数组。

如果您只想指定最后一维的前几个元素,可以使用冒号(:)字符来对 NumPy 数组进行切片:

> > >
> > > arr[:, :, 0]
array([[0, 2],
       [4, 6]])

解释: 由于数组有三个维度,因此您必须提供三个切片;如果添加了其他维度,语法会变得非常麻烦!未能确定数组的维度将使情况更加糟糕。

> > >
> > > import numpy as npp

> > > dimensions = npp.random.randint(1,10)
> > > items_per_dimensions = 2
> > > max_items1 = items_per_dimensions**dimensions
> > > axe = npp.repeat(items_per_dimensions, dimensions)
> > > arrr = npp.arange(max_items1).reshape(axe)
You are making an array in this example, which can have up to ten dimensions. The.ndim() function in NumPy can be used to determine how many dimensions arrr has. But in this instance, utilizing... is preferable:
> > >
> > > arr[..., 0]
array([[[[ 0,  2],
         [ 4,  6]],

        [[ 8, 10],
         [12, 14]]],


       [[[16, 18],
         [20, 22]],

        [[24, 26],
         [28, 90]]]])

解释: 在这种情况下,数组元素有五个维度。由于维度数量是随机的,因此您的结果可能会有所不同。然而,使用省略号(…)来指定您的多维数组将起作用。

NumPy 还提供了其他选项,可以使用省略号来指示一个元素或一系列数组。有关这三个小点的其他应用,请参见 NumPy: Ellipsis (…) for ndarray。

Python 中的省略号是否总是三个点?

在学习了 Python 中的省略号之后,您可能会更仔细地注意到出现在 Python 宇宙中的每个省略号。三个点可能会出现在 Python 中,但它们可能不是 Ellipsis 常量。

Python 交互式 shell 中的三个点表示辅助提示符:

> > >
> > > def hello_world():
...     print("Hello, world!")
...

提示符可能会跨越多行,例如在 Python 程序中定义函数或编写 for 循环时。

上面提到的三个点不是字面意义上的省略号;它们是您函数体的额外提示符。

队列结束标记

省略号有时用于表示一行的结尾。这是合理的:

即使在队列中通常不会看到它,它也足够独特,可以作为队列的末尾标记,因此您不需要构建自定义对象。

重复引用

在 REPL 中,Python 对于循环引用会显示一个省略号。

>>>a1 = [0, 1]
>>> a1[0] = a1
>>> a1
[[...], 1]

这并不意味着 Python 用省略号替换引用;相反,它只是显示一个省略号,而不会崩溃或让您的屏幕溢出。您可以自己检查这个问题。如果您继续请求嵌套对象,原始的 a 仍然会返回:

>>>a1 = [0, 1]
>>> a1[0] = a1
>>> a1[0][0][0][0][0][0][0][0][0][0]
[[...], 1]

结论

字面值 “ellipsis”(…)将计算为 Ellipsis 值。在创建函数 stub 时,您最常使用省略号作为占位符。

当您希望具有灵活性时,类型提示中的三个点可能非常有用。省略号字面值可以将可调用类型的参数列表替换为同类型的可变长度元组。

使用 NumPy,您可以通过将 Ellipsis 对象替换为可变长度维度来简化切片语法。由于三个点的无冗余语法,您的代码将更容易理解。

Python教程

Java教程

Web教程

数据库教程

图形图像教程

大数据教程

开发工具教程

计算机教程

Python 教程