您遇到的问题是 five 仅在此语句中 three 为 True 时分配,因为 short circuiting:
if (three:= i%3==0) and (five:= i%5 ==0)
所以five 通常未被分配,导致NameError 或使用非当前值。
您可以通过在其中形成一个包含海象分配的非空元组,然后在该元组之后使用您所期望的 three 和 five 来强制一个 True 值。
这并不比在if 之前分配three 和five 更漂亮,但这有效:
arr=[]
for i in range(1,26):
if (three:=i%3==0, five:=i%5==0) and three and five:
arr.append(f"{i} FizzBuzz")
elif three:
arr.append(f"{i} Fizz")
elif five:
arr.append(f"{i} Buzz")
else:
arr.append(f"{i}")
>>> arr
['1', '2', '3 Fizz', '4', '5 Buzz', '6 Fizz', '7', '8', '9 Fizz', '10 Buzz', '11', '12 Fizz', '13', '14', '15 FizzBuzz', '16', '17', '18 Fizz', '19', '20 Buzz', '21 Fizz', '22', '23', '24 Fizz', '25 Buzz']
任何非空元组在 Python 中都是 True。形成它会导致(three:=i%3==0, five:=i%5==0) 始终是真实的,并且每次都分配三个和五个。由于该元组为真,因此必须使用正确的值 3 和 5 来评估表达式的其余部分。
或者,使用if all((three:=i%3==0, five:=i%5==0)):,因为元组是在测试其内容之前形成的——即使all短路;这只会在元组形成后发生。
这些形式中的任何一种都可以轻松重构为理解:
arr=[f"{i} FizzBuzz" if three and five
else f"{i} Fizz" if three
else f"{i} Buzz" if five
else f"{i}"
for i in range(1,26) if (three:=i%3==0, five:=i%5==0)]
或者,
arr=[f"{i} FizzBuzz" if all((three:=i%3==0, five:=i%5==0))
else f"{i} Fizz" if three
else f"{i} Buzz" if five
else f"{i}" for i in range(1,26)]
如果 each 元素的结果不是布尔值,请注意构造 if (three := i % 3 == 0) & (five := i % 5 == 0):。你可能会遇到一些意想不到的失败:
>>> bool((x:=3) & (y:=4))
False
>>> bool((x:=3) and (y:=4))
True
解决此问题的唯一方法是将bool 应用于每个:
>>> bool(x:=3) & bool(y:=4)
True
顺便说一句,说到元组,一种在 Python 中进行 FizzBuzz 类型挑战的更短的方法:
fb={(True,True):"{} FizzBuzz",
(True,False):"{} Fizz",
(False,True):"{} Buzz",
(False,False):"{}"}
arr=[fb[(i%3==0,i%5==0)].format(i) for i in range(1,26)]
如果你正在寻找新的东西,这种类型的问题对于 Python 3.10+ 来说是很自然的pattern matching:
arr=[]
for i in range(1,26):
s=f"{i}"
match (i%3==0,i%5==0):
case (True, (True | False) as oth):
s+=" FizzBuzz" if oth else " Fizz"
case (False, True):
s+=" Buzz"
arr.append(s)