Python中的星号符号
我在研究解决经典的FizzBuzz问题的不同方法时,偶然发现了这个:
for i in xrange(1, n+1):
print "Fizz"*(i%3 == 0) + "Buzz"*(i%5 == 0) or i
这些星号是if
语句的简写吗?如果是的话,这种写法是特定于print
的吗?
提前谢谢你。
2 个回答
在Python中,星号(asterisk)用来表示乘法。如果它用在字符串上,它会把这个字符串重复指定的次数。例如:
print "test"*0
print "test"*1
print "test"*2
输出结果:
<blank line>
test
testtest
在你的例子中,有一个or
条件,它是从左到右进行判断的:
print "Fizz"*(i%3 == 0) + "Buzz"*(i%5 == 0) or i
如果Fizz
或Buzz
的值不为零或为真,Python就不会去判断i
的值。
当i
是3的倍数时,Fizz
的值为真(所以,对于所有能被3整除的i
值,它会输出3)。
同样,Buzz
在所有5的倍数时为真。
当i
能被15整除时,Fizz
和Buzz
都会输出。
只有当上述两个条件都为假时,i
才会被计算并打印出来。
在Python中,星号(*)其实就是标准的乘法符号。它对应的是一个叫做 __mul__
的方法,这个方法是作用在它后面的对象上的。因此,我们可以自定义这个符号的含义。这和 if
或 print
没有任何关系。
对于字符串(str
和 unicode
),这个星号被重新定义为表示字符串的重复,比如 "foo" * 5
会变成 "foofoofoofoofoo"
。
>>> 'foo' * 5 # and the other way around 5 * "foo" also works
'foofoofoofoofoo'
而 "Fizz" * (i % 3 == 0)
其实是一个“聪明”的简写,等同于:
"Fizz" if i % 3 == 0 else ""
这是因为表达式 i % 3 == 0
的结果是一个布尔值,而布尔值在Python中是整数的一个子类型,所以 True == 1
和 False == 0
。因此,如果你用布尔值去“乘”一个字符串,你要么得到原来的字符串,要么得到一个空字符串。
注意:我想补充一下,根据我的经验,这种编程风格在Python中并不被推荐——这样会让代码变得不易阅读(对于新手和老手都是如此),而且也不会让代码运行得更快(实际上可能会更慢;可以参考这个快速基准测试:http://pastebin.com/Q92j8qga,有趣的是在PyPy中却不是这样:http://pastebin.com/sJtZ6uDm)。
星号(*)同样适用于 list
和 tuple
的实例:
>>> [1, 2, 3] * 3
[1, 2, 3, 1, 2, 3, 1, 2, 3]
>>> (1, 2, 3) * 3
(1, 2, 3, 1, 2, 3, 1, 2, 3)
你还可以为自己的类型定义星号(*)操作符,这实际上就是在Python中进行的 运算符重载:
class Foo(object):
def __mul__(self, other):
return "called %r with %r" % (self, other)
print Foo() * "hello" # same as Foo().__mul__("hello")
输出:
called <__main__.Foo object at 0x10426f090> with 'hello'
对于像 int
、float
这样的“基本”类型,星号(*)的映射到 __mul__
的情况也是一样的,所以 3 * 4
等同于 (3).__mul__(4)
(其他运算符也是如此)。实际上,你甚至可以继承 int
并为星号(*)提供自定义的行为:
class MyTrickyInt(int):
def __mul__(self, other):
return int.__mul__(self, other) - 1
def __add__(self, other):
return int.__add__(self, other) * -1
print MyTrickInt(3) * 4 # prints 11
print MyTrickyInt(3) + 2 # prints -5
...但请不要这样做 :) (实际上,尽量避免继承具体类型是个好主意!)