I was researching some different ways to approach the classic FizzBuzz problem, and stumbled across this:
for i in xrange(1, n+1):
print "Fizz"*(i%3 == 0) + "Buzz"*(i%5 == 0) or i
Are the asterisks shorthand for an if
statement? If so, is this notation specific to print
?
Thanks in advance.
The asterisk in Python is actually just the standard multiplication operator *
. It maps to the __mul__
method of the object it's operated on, and thus can be overloaded to have custom meanings. This has nothing to do whatsoever with if
or print
.
In the case of strings (str
and unicode
) it has been overloaded/overridden to mean repetition of strings such that "foo" * 5
evaluates to "foofoofoofoofoo"
.
>>> 'foo' * 5 # and the other way around 5 * "foo" also works
'foofoofoofoofoo'
and "Fizz" * (i % 3 == 0)
is simply a "smart" shorthand for:
"Fizz" if i % 3 == 0 else ""
This is because the expression i % 3 == 0
evaluates to a boolean, and booleans are a subtype of integers in Python, so that True == 1
and False == 0
, so that if you "multiply" a string with a boolean, you'll either get the same string out or the empty string.
Note: I would also like to note that in my experience/understanding, this type of programming style is not encouraged in Python—it makes the code less readable (to both newcomers as well as oldtimers) as well as not any faster (and in fact, likely, slower; see http://pastebin.com/Q92j8qga for a quick benchmark (but interestingly, not in PyPy: http://pastebin.com/sJtZ6uDm)).
And *
also works on instances of list
and 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)
You can also define your own *
operators for your types using what amounts to operator overloading in Python:
class Foo(object):
def __mul__(self, other):
return "called %r with %r" % (self, other)
print Foo() * "hello" # same as Foo().__mul__("hello")
outputs:
called <__main__.Foo object at 0x10426f090> with 'hello'
The case of *
being mapped to __mul__
also holds for "primitive" types such as int
, float
and others, so 3 * 4
is equivalent to (3).__mul__(4)
(and same about other operators). In fact, you can even subclass int
and provide a custom behavior for *
:
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
...but please don't do that :) (in fact, it doesn't hurt to stay clean of subclassing concrete types altogether!)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With