Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does it work?

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?

like image 536
Lone Wolves Avatar asked Jan 08 '23 20:01

Lone Wolves


2 Answers

There are no ifs, no elifs, nothing.

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 becomes 1 and False becomes 0. So

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

means "One Fizz if i % 3 == 0, none if not." Same with Buzz.

Finally, that or i at the end means that if you got the empty string because both parts came up empty, then you get i 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's

a? b: c

which means "b if a is true, otherwise c" becomes this in Python:

(c, b)[a]
like image 199
Mike DeSimone Avatar answered Jan 17 '23 13:01

Mike DeSimone


Break it out and you'll understand it.

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)

String multiplication repeats the string, so 'a' * n is aaaaa...n times...a. if i % 3 != 0, then does_it_fizz(i) returns 0. "any string" * 0 == "". If the number should neither Fizz nor Buzz, you get print("" or num). The empty string is Falsey, but num is always Truthy, so it prints num

like image 22
Adam Smith Avatar answered Jan 17 '23 14:01

Adam Smith