ale*_*inn 5 python python-3.8 python-assignment-expression
在我看来,将经典的while循环与Assignment-expressions -loops互换可以使代码看起来很棒不是那么简单。
考虑example1:
>>> a = 0
>>> while (a := a+1) < 10:
... print(a)
...
1
2
3
4
5
6
7
8
9
Run Code Online (Sandbox Code Playgroud)
和example2:
>>> a = 0
>>> while a < 10:
... print(a)
... a += 1
...
0
1
2
3
4
5
6
7
8
9
Run Code Online (Sandbox Code Playgroud)
您将如何修改example1以具有相同的输出(不跳过0)example2?(a = 0当然,无需更改)
Mar*_*ers 17
像您的示例这样的简单循环不应使用赋值表达式。PEP有一个样式指南建议部分,您应注意以下几点:
- 如果可以使用赋值语句或赋值表达式,则首选语句。它们是意图的明确声明。
- 如果使用赋值表达式会导致执行顺序含糊不清,则将其重组为使用语句。
简单的循环应该使用iterables和来实现for,它们显然要循环执行直到迭代器完成。对于您的示例,选择的可迭代项为range():
for a in range(10):
# ...
Run Code Online (Sandbox Code Playgroud)
这是远更清洁和简明易读比,也就是说
a = -1
while (a := a + 1) < 10:
# ...
Run Code Online (Sandbox Code Playgroud)
上面需要进行额外的检查,才能确定循环中的 a起始于0,而不是起始于-1。
最重要的是,您不应被诱使“寻找使用赋值语句的方法”。仅当赋值语句使代码更简单而不复杂时才使用赋值语句。没有while比for这里的循环更简单的好方法了。
Tim Peters的发现附录也呼应了您尝试重写一个简单循环的尝试,该附录引用了Tim Peters关于样式和赋值表达式的主题。蒂姆·彼得斯(Tim Peters)是Python的Zen的作者(在对Python和整个软件工程的许多其他伟大贡献中),因此他的话应具有额外的分量:
在其他情况下,将相关的逻辑组合在一起会更难理解,例如重写:
Run Code Online (Sandbox Code Playgroud)while True: old = total total += term if old == total: return total term *= mx2 / (i*(i+1)) i += 2作为简介:
Run Code Online (Sandbox Code Playgroud)while total != (total := total + term): term *= mx2 / (i*(i+1)) i += 2 return total
while那里的测试太微妙了,关键是要在非短路或方法链接的情况下严格依赖从左到右的评估。我的大脑没有那样连线。
大胆强调我的。
赋值表达式的一个更好的用例是“先贴后测试”模式,尤其是在需要进行尝试连续对象的多个测试时。Tim的文章引用了标准库中Kirill Balunov给出的示例,该示例实际上受益于新语法。该copy.copy()函数必须找到合适的钩子方法来创建自定义对象的副本:
reductor = dispatch_table.get(cls)
if reductor:
rv = reductor(x)
else:
reductor = getattr(x, "__reduce_ex__", None)
if reductor:
rv = reductor(4)
else:
reductor = getattr(x, "__reduce__", None)
if reductor:
rv = reductor()
else:
raise Error("un(shallow)copyable object of type %s" % cls)
Run Code Online (Sandbox Code Playgroud)
缩进是嵌套if语句的结果,因为Python在找到一个选项之前不会给我们提供更好的语法来测试不同的选项,并且同时将选定的选项分配给一个变量(您不能在此处干净地使用循环)因为并非所有测试都针对属性名称)。
但是赋值表达式使您可以使用平面 if / elif / else结构:
if reductor := dispatch_table.get(cls):
rv = reductor(x)
elif reductor := getattr(x, "__reduce_ex__", None):
rv = reductor(4)
elif reductor := getattr(x, "__reduce__", None):
rv = reductor()
else:
raise Error("un(shallow)copyable object of type %s" % cls)
Run Code Online (Sandbox Code Playgroud)
这8条线比目前的13条线更干净,更容易(在我看来)。
另一个经常被引用的好用例是:过滤后是否存在匹配的对象,对该对象做点什么,当前需要具有生成器表达式,默认后备值和测试的next()函数if:
found = next((ob for ob in iterable if ob.some_test(arg)), None)
if found is not None:
# do something with 'found'
Run Code Online (Sandbox Code Playgroud)
if any((found := ob).some_test(arg) for ob in iterable):
# do something with 'found'
Run Code Online (Sandbox Code Playgroud)