【问题标题】:Python slice/index evaluation order (before list definition)Python 切片/索引评估顺序(在列表定义之前)
【发布时间】:2022-01-22 10:17:04
【问题描述】:

我想知道 python 在定义列表/元组之后评估切片/索引是否有原因?

(这个问题与 Python Code Golf 有关,所以我知道它不可读或“良好做法”,它只是关于可接受的语法和语言的基本行为。)

我们可以使用索引来模拟三元运算符的行为:

a if x > 9 else b
(b,a)[x>9]  # way shorter

但这有一个问题:元组的内容在索引中的条件之前被评估。

我创建了一个示例来说明这一点:一个函数f,它递归地将字符串的大小减一,直到字符串为空,然后返回0

f = lambda s: f(s) if len(s:=s[:-1]) else 0
print(f("abc"))  # works fine

f = lambda s: (0, f(s))[len(s:=s[:-1])==0]
print(f("abc"))  # max recursion depth error

出现递归深度错误是因为在索引之前评估元组定义。这意味着切片/索引中的内容无关紧要,该函数将被一遍又一遍地调用。

我真的不明白为什么 python 之前不评估切片/索引,因为即使是像下面这样的明显情况也会失败:

f = lambda: (0, f())[0]
f()  # max recursion depth error

最重要的是,如果我们只评估我们想要从数组中获取的单个元素(或切片)而不是每个单个元素,它可能会在内存使用和运行时间方面受益:

x = 2
print([long_computation(), other_long_computation(), 0][x])

有什么理由不在元组定义之前评估切片/索引吗?

【问题讨论】:

  • 你不能切片还不存在的东西。 (另外,这是索引,而不是切片。)
  • @user2357112supportsMonica 是的,它更多的是关于索引 :) 元组可以存在但不评估其元素,有点像 f = lambda s: (lambda: 0, lambda: f(s))[len(s:=s[:-1])==0]() 那样有效
  • 所以你真的想要一个对其成员进行惰性求值的元组吗?在 Python 中可能有一种方法可以做到这一点,但我不知道怎么做,而且肯定不值得麻烦。
  • @MarkRansom 是的,当[...] index/slice 直接跟随时,我想要惰性元组评估,因为我不明白为什么要评估其余部分,因为它会被切片留下,评估切片后直接保留元素会发生
  • 但这对代码高尔夫有用,这是 Python 开发团队无意鼓励的用例。对于不打代码的人来说,它只会增加语言的复杂性和奇怪的边缘情况。

标签: python syntax


【解决方案1】:

人类从左到右读取,解析器从左到右读取,编译器只是将解析的内容转换为字节码。只从左到右解析它而不是为已经以从左到右的方式正确完成的东西添加特殊情况是有意义的——而且更容易。为什么你仍然需要这样做?您不需要 这样做。您已经可以在当前的解析器和编译器中正确执行此操作。这种特殊情况的复杂性以及它曾经使用过的罕见程度,这两个因素都足以作为不在元组定义之前评估切片的理由。

【讨论】:

  • 三元运算符是一个反例,不是从左到右计算的。而且我不需要说明我为什么想要某样东西,我只想要答案……
  • @Gruvw 我给了你另一个答案:它曾经被使用的多么罕见是一个在元组定义之前不评估切片的原因。你也可以在 python 想法邮件列表中提出这个建议,但是我在这篇评论中提到的原因可能已经给你了。
  • @Gruvw 也会破坏一些依赖于首先评估元组的代码。
  • 我不同意你的看法,因为我经常使用这样的元组和索引(不经常使用递归,但我也用long_computation() 给出了一个简单的例子)。然而,向后兼容性是这个想法的最大问题。话虽如此,我的问题更多是关于为什么 python 这样做,而不是我们需要改变它。
  • 反正你在cmet中已经有了答案,我就不争辩了。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-06-28
  • 1970-01-01
  • 1970-01-01
  • 2021-12-02
  • 2013-12-10
  • 1970-01-01
相关资源
最近更新 更多