为什么它有效?

Why does it work?

所以我正在学习 Python 并研究一系列程序创意。当然,我写了强制性的 FizzBu​​zz,它起作用了,但基本上是 if elif else blablabla。我在谷歌上搜索了一下,看看是否还有其他方法,然后发现了这个阴暗的单线:

for i in range(1,101): 
    print("Fizz" * (i % 3 == 0) + "Buzz" * (i % 5 == 0) or i)

没有ifs,没有elifs,什么都没有。我用谷歌搜索 "string concatenation" 并找到了有关 * 符号的信息,但不明白它在这种情况下是如何工作的。有人可以解释吗?

一旦 if (i % 5 == 0) 为真或 return i:

,您将打印字符串 "Buzz"
In [5]: "foo" * 2
Out[5]: 'foofoo'

In [6]: "foo" * 3
Out[6]: 'foofoofoo'

In [7]: i = 5
In [8]: "foo" *  (i % 5 == 0) or i
Out[9]: 'foo'  
In [9]: "foo" *  (i % 5 == 1) or i
Out[22]: 5

同样的逻辑适用于 "Fizz" 有时 (i % 3 == 0) 会是 True 所以我们看到一次,当它是 False 时我们就看不到它。

当您在字符串上使用 * 运算符时,它将重复字符串 n 次,在这种情况下,它最多只会重复一次,因为任一字符串仅根据布尔测试。

您可以在 ipython 中看到 TrueFalse 发生了什么:

In [26]: "foo" * True
Out[26]: 'foo'

In [27]: "foo" * False
Out[27]: ''

基本上True * "foo"相当于1 * "foo""foo" * False相当于0 * "foo"

Bool 是 int 的子类,因此代码利用了这一事实,您有时会看到类似的逻辑用于基于测试索引列表,但不推荐:

In [31]: d = ["bar","foo"]   
In [32]: d[3<2] # False so we get 0 the first element
Out[32]: 'bar'   
In [33]: d[3>2] # True so we get 1 the second element
Out[33]: 'foo'

拆开你就明白了

def does_it_fizz(num):
    return num % 3 == 0

def does_it_buzz(num):
    return num % 5 == 0

for num in range(1, 101):
    print("Fizz" * does_it_fizz(num) + "Buzz" * does_it_buzz(num) or num)

字符串乘法重复字符串,所以'a' * naaaaa...n times...a。如果 i % 3 != 0,则 does_it_fizz(i) returns 0"any string" * 0 == ""。如果数字既不是 Fizz 也不是 Buzz,您将得到 print("" or num)。空字符串是 Falsey,但是 num 总是 Truthy,所以它打印 num

i == 3 时,(i % 3 == 0) 将为真。

任何字符串 * True 都会 return 字符串。在这种情况下,将 True 视为整数 1 会有所帮助(因为乘以 1 的任何东西都是原始乘积)。

当例如。 i == 1(i % 3 == 0) 将为 False (as 1 % 3 == 1)

所以string * False ==空字符串

获取上面 return 编辑的 fizz 和 buzz 字符串,并使用 + 运算符将它们连接起来。

现在在 print 语句中,如果连接的结果是空字符串,or 运算符将使用 i 的值。

下面是对正在发生的事情的细分:

for i in range(1,101):
    if (i % 3 == 0):
        print "Fizz"
    if (i % 5 == 0):
        print "Buzz"
    if (i % 5 != 0 and (i % 3 != 0)):
        print i

我的 ide 设置为 Python 2.7 btw

There are no ifs, no elifs, nothing.

当然有!只是伪装。寻找字符串连接(即 +)对您没有帮助,因为 * 重复 。具体来说,string * n 给你一个字符串,它是连续 n 个字符串的副本。此外,布尔值可以隐式转换为整数:True 变为 1False 变为 0。所以

"Fizz" * (i % 3 == 0)

表示"One Fizz if i % 3 == 0, none if not."与Buzz相同。

最后,最后的 or i 意味着如果你得到的是空字符串,因为两部分都是空的,那么你会得到 ior 真正的意思是 "the value of the left hand side unless the left hand side is a false value, in which case return the value of the right hand side."

这个技巧也被用在其他地方。 Python 没有直接等同于 C 的 ?: 运算符,但是由于我上面提到的 bool-to-integer 转换,您可以使用一个二元素元组和一个索引操作接近一个.所以C的

a? b: c

意思是“b如果a为真,否则c”在Python中变成这样:

(c, b)[a]

如果你只考虑条件:

(i % 3 == 0)

这会生成一个布尔值,说明 i 模 3 的特定值是否等于 0。这是 3 的倍数或 0(0、3、6 等)的情况。所以这就是你如何知道打印 "Fizz"(或 "Buzz",给定其他条件)。

Python 中关于字符串的酷炫之处在于您可以有条件地打印字符串。例如,启动解释器并输入:

'foo' * True
'foo' * False

这应该会产生以下输出:

'foo'
''

所以基本上,对于给定的 i 值,您正在这样做:

print("Fizz" * (0 % 3 == 0) + "Buzz" * (0 % 5 == 0) or i) -> print("Fizz" * True + "Buzz" * True or i) -> printf('Fizz'+'Buzz')
print("Fizz" * (1 % 3 == 0) + "Buzz" * (1 % 5 == 0) or i) -> print("Fizz" * False + "Buzz" * False or i) -> printf(1)
print("Fizz" * (2 % 3 == 0) + "Buzz" * (2 % 5 == 0) or i) -> print("Fizz" * False + "Buzz" * False or i) -> printf(2)
print("Fizz" * (3 % 3 == 0) + "Buzz" * (3 % 5 == 0) or i) -> print("Fizz" * True + "Buzz" * False or i) -> printf("Fizz")
....

这就是 "dank" 一个衬垫的工作原理。