So I'm learning Python and working through a list of program ideas. Of course I wrote the obligatory FizzBuzz, which worked, but was basically if elif else blablabla. I googled it to see if there are other ways and found this dank one-liner:
for i in range(1,101):
print("Fizz" * (i % 3 == 0) + "Buzz" * (i % 5 == 0) or i)
There are no ifs, no elifs, nothing. I googled "string concatenation" and found info on the * symbol, but don't understand how it's working in this case. Can somebody explain?
Break it out and you'll understand it.
String multiplication repeats the string, so
'a' * n
isaaaaa...n times...a
. ifi % 3 != 0
, thendoes_it_fizz(i)
returns0
."any string" * 0 == ""
. If the number should neither Fizz nor Buzz, you getprint("" or num)
. The empty string is Falsey, butnum
is always Truthy, so it printsnum
Here is a breakdown of what is happening:
My ide is set to Python 2.7 btw
If you consider just the conditional:
this generates a boolean which states whether that particular value of i modulo 3 is equal to 0. This is the case for a multiple of 3, or 0 (0, 3, 6, etc). So this is how you would know to print "Fizz" (or "Buzz", given the other conditional).
The cool thing about strings in Python is that you can conditionally print a string. For instance, fire up an interpreter and type this:
This should result in the following output:
So basically, for a given value of i, you're doing this:
This is how the "dank" one liner works.
Sure there are! Just disguised. Looking for string concatenation (i.e.
+
) won't help you, because*
is repetition. Specifically, string*
n gives you a string that is n copies of string in a row. Further, a boolean value can be implicitly converted to an integer:True
becomes1
andFalse
becomes0
. Someans "One
Fizz
ifi % 3 == 0
, none if not." Same withBuzz
.Finally, that
or i
at the end means that if you got the empty string because both parts came up empty, then you geti
instead.or
really means "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."This trick gets used elsewhere, too. Python doesn't have a direct equivalent to C's
?:
operator, but you can get close to one with a two-element tuple and an index operation because of the bool-to-integer conversion I mentioned above. So C'swhich means "
b
ifa
is true, otherwisec
" becomes this in Python:When
i == 3
, the(i % 3 == 0)
will be True.Any string * True will return the string. It helps to think of
True
as the integer1
in this case (as anything multiplied by1
is the original thing that was multiplied).When eg.
i == 1
, the(i % 3 == 0)
will be False(as 1 % 3 == 1)
.So
string * False
== empty stringTake the fizz and the buzz strings that were returned above and concatenate them using the
+
operator.Now in the print statement, if the result of that concatenation is an empty string, the
or
operator will use the value ofi
instead.You are going to print the string
"Buzz"
either onceif (i % 5 == 0)
is True or returni
:The same logic applies to "Fizz" sometimes
(i % 3 == 0)
will be True so we see it once, when it is False we don't see it.When you use the
*
operator on a string it will repeat the stringn
times, in this case it will once at most as either string is only printed based on the result of the boolean test.You can see in ipython exactly what happens with
True
andFalse
:Basically
True * "foo"
is equivalent to1 * "foo"
"foo" * False
is equivalent to0 * "foo"
Bool is a subclass of int so the code is taking advantage of that fact, you sometimes see similar logic used with indexing a list based on a test although not recommended: