为什么不允许使用增强赋值表达式?



我最近阅读了关于赋值表达式的PEP 572,偶然发现了一个有趣的用例:

# Compute partial sums in a list comprehension
total = 0
partial_sums = [total := total + v for v in values]
print("Total:", total)

我开始自己研究这个片段,很快发现:+=不是有效的Python语法。

# Compute partial sums in a list comprehension
total = 0
partial_sums = [total :+= v for v in values]
print("Total:", total)

我怀疑:=是如何实现的,可能有一些潜在的原因明智地排除了:+=,但我不确定它可能是什么。如果在Python方面更聪明的人知道:+=为什么不可行、不切实际或在其他方面没有实现,请分享你的理解。

简短版本:添加walrus操作符极具争议,他们希望阻止过度使用,因此仅限于提出激励用例的情况,使=成为所有其他情况的方便工具

walrus操作符有很多事情是不能做的(分配给在序列或映射中查找到的东西,分配给属性等),但它会鼓励一直使用它,这会损害典型代码的可读性。当然,你可以选择写可读性更强的代码,忽略使用奇怪和糟糕的标点符号的机会,但如果我(和许多人)使用Perl的经验可以作为指导,人们现在会使用快捷方式更快地完成它,即使一个月后生成的代码无法读取,甚至他们也无法读取。

还有其他一些小障碍(用walrus支持所有增强的分配方法将向解释器添加大量新的字节码,显著扩展eval循环的switch,并可能抑制CPU缓存的优化/溢出),但从根本上讲,将列表理解用于副作用的动机是滥用列表理解(与所有函数式编程工具一样,这是一种函数结构,不打算产生副作用),就像大多数依赖于增强赋值表达式的情况一样。引入这一功能的强烈动机是你可以合理地想做的事情,如果没有海象,你就做不到,例如

  1. Regexes,取代了这个可怕的、冗长的箭头模式:

    m = re.match(r'pattern', string)
    if m:
    do_thing(m)
    else:
    m = re.match(r'anotherpattern', string)
    if m:
    do_another_thing(m)
    else:
    m = re.match(r'athirdpattern', string)
    if m:
    do_a_third_thing(m)
    

    有了这个干净的测试链:

    if m := re.match(r'pattern', string):
    do_thing(m)
    elif m := re.match(r'anotherpattern', string):
    do_another_thing(m)
    elif m := re.match(r'athirdpattern', string):
    do_a_third_thing(m)
    
  2. 按块读取文件,替换:

    while True:
    block = file.read(4096)
    if not block:
    break
    

    清洁:

    while block := file.read(4096):
    

这些都是人们真正需要经常做的有用的事情,甚至是;规范的";我发布的版本经常以其他方式被错误地实现(例如,应用regex测试两次以避免箭头模式,在循环之前复制block = file.read(4096)一次,在循环结束时复制一次,这样你就可以运行while block:,但作为交换,现在continue不能正常工作,你有可能在一个地方改变块的大小,而不是在另一个地方);walrus运算符允许更好的代码。

listcomp累加器并不是更好的代码。itertools.accumulate存在,即使它不存在,也可以通过简单的生成器函数或手动循环以其他方式解决问题。政治公众人物确实将其描述为一种好处(这就是为什么他们允许海象任务"逃避"理解),但与这一范围界定特例有关的讨论甚至比添加海象本身的讨论更为分歧;你可以这样做,这可以说是有用的,但这不是你看到两个选项就立即说";天啊,如果不是海象,这将是可怕的"。

为了满足那些想要这方面证据的人,请注意,他们明确屏蔽了一些本可以免费使用的用例(需要额外的工作才能使语法禁止这些用法),特别是为了防止海象过度使用。例如,不能执行:

x := 1

在一条线上。没有任何技术原因不能这样做,但他们故意让海象在没有被包裹的情况下使用成为语法错误。(x := 1)在顶级水平上工作,但这已经够烦人的了,没有人会选择它而不是x = 1,这就是我们的目标。

除非有人提出一个公共代码模式,因为缺乏:+=,它变得不可行/不必要地丑陋(而且必须是真正的公共和非常的TR

itertools模块提供了许多机制来在不使语言膨胀的情况下实现相同的结果:

partial_sums = list(accumulate(values))
total        = partial_sums[-1] 
  1. 我的理解是理解效率的一个主要部分是原始可迭代中每个元素的计算可以并行执行。如果你正在计算一个连续的总数,那么必须按顺序执行。

  2. 我们已经有了sum(values),那么在这个用例中添加了什么呢?

  3. 如果您想要更通用的用例,functoolsreduce

最新更新