【问题标题】:What is the meaning of this idiom assigning from an empty list to an empty list?这个成语从一个空列表分配到一个空列表是什么意思?
【发布时间】:2022-01-05 18:15:55
【问题描述】:

我遇到了一个调用函数 f() 的代码:

def f():
    return 1, []

a, [] = f()

我想知道为什么作者不使用a, _ = f()。 为什么甚至允许这种语法?

[] = []
a, [] = 444, []

我在哪里可以阅读更多关于[] = [] 哲学的信息?

【问题讨论】:

  • 这很奇怪。它不会做任何事情,并且做a, _ = f() 绝对是首选方式。我会非常怀疑您从哪个网站获得此信息。
  • stackoverflow.com/questions/5182573/…。您可以分配给左侧的多个元素,这通常使用元组完成:a, b = [1, 2],与(a, b) = [1, 2] 相同。您还可以使用左侧的列表语法:[a, b] = [1, 2]。在这里,您正在使用左侧的 0 项列表来执行此操作,而右侧也没有任何项:[] = [] 因此是有效的。不过,我看不出它有多大用处……
  • [] = [][a, b, c] = [x, y, z] 的简并情况。通常它将 some 值解压缩为 some 名称,但它也可能将无值解压缩为无名称。
  • 感谢您的回答。所以基本上,[] = [] 性能较差,因为必须构造一个空列表。对吗?
  • @TomB。我认为解包通常使用元组完成,因此 () 可能比作业左侧的 [] 更好。与_ 相比的不同之处在于,如果列表不为空,它将引发异常(ValueError),这可能是您想要的检查。

标签: python syntax


【解决方案1】:

考虑一个看起来不那么奇怪的例子:

def f():
    return 1, [2, 3]

a, [b, c] = f()

这是一个普通的destructuring assignment;我们简单地同时分配a = 1b = 2c = 3。左边的结构和右边的结构一致(在左边,我们可以交替使用[](),对应的list和tuple对象其实是不存在的- 或在任何时候;它们在这里纯粹是句法)。

您显示的代码只是这种情况的简并情况,其中子序列中的元素为零。 1 分配给a,所有零元素[] 分配给零个目标名称。

我想知道作者为什么不用a, _ = f()

我无法读懂作者的想法,但一个可能的原因:

def f():
    return 1, g()

# later:
# precondition: the result from g() must be empty for code correctness
a, [] = f() # implicitly raises ValueError if the condition is not met

这很容易写,但需要一些解释,也许不是最好的方法。 Explicit is better than implicit.

为什么甚至允许这种语法

因为special cases aren't special enough to break the rules

【讨论】:

    【解决方案2】:

    语法[] = []assignment to a target list 的一个边缘情况:它将空可迭代的值解包为零目标名称。


    Python 的赋值语句允许多重赋值,通过对目标使用列表或元组语法。源可以是具有正确数量的项目的任何可迭代对象。

    >>> a, b = range(2)
    >>> a
    0
    >>> (a, b) = {1: "one", 2: "two"}
    >>> a
    1
    >>> [a, b] = {3, 5}
    >>> a
    3
    

    值得注意的是,在这种情况下,左侧并不表示实际的元组/列表。它只定义了实际分配目标ab 所在的结构,类似于模式匹配。

    作为边缘情况,语法还允许指定一个或零长度的分配列表。

    >>> # assign single-element iterable into single name
    >>> [a] = {15}
    >>> a
    15
    >>> # assign no-element iterable into no name
    >>> [] = []
    

    值得指出的是,左手边和右手边[] 是根本不同的东西。 右侧 手边... = [] 表示没有元素的实际list 对象。 手边[] = ...仅表示“零名称”。


    多重分配通常有两个目的,即在检查项目数量的同时执行实际分配。

    >>> # accept any number of secondary items
    >>> a, b = 42, [16, 72]
    >>> # accept only one secondary item
    >>> a, [b] = 42, [16, 72]
    ...
    ValueError: too many values to unpack (expected 1)
    

    通过使用一个空的目标列表,可以强制一个可迭代的但它是空的

    >>> # empty iterable: fine
    >>> a, [] = 444, []
    >>> # non-empty iterable: error
    >>> a, [] = 444, ["oops"]
    ...
    ValueError: too many values to unpack (expected 0)
    

    【讨论】:

    • 请问(a, b) = ...[a, b] = ...之间没有性能差异吗?
    • @alani 是的,两者之间的区别纯粹是句法上的。不涉及元组/列表,含义100%相同。
    • 谢谢 - 知道有用。
    【解决方案3】:

    如前所述,由于 Python 中所谓的“模式匹配”,这种语法是允许的。在这种特殊情况下,列表是空的,因此 Python 不匹配任何内容。如果列表中有更多元素,这实际上会有所帮助,正如 Karl Knechtel 在他的回答中所显示的那样。

    您的示例只是模式匹配(用于匹配 0 个元素)的一个特殊(且无用)案例。

    【讨论】:

    猜你喜欢
    • 2015-07-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-06
    • 1970-01-01
    • 1970-01-01
    • 2011-12-06
    相关资源
    最近更新 更多