What is the star (*) doing in this FizzBuzz solution? - python

Learning programming in Python and I am doing some challenges. I've run into something I haven't learned yet and was curious what this code is doing.
So my challenge is called the "FizzBuzz" challenge. The instructions are simple:
Create a function that takes a number as an argument and returns
"Fizz", "Buzz" or "FizzBuzz".
If the number is a multiple of 3 the output should be "Fizz". If the
number given is a multiple of 5, the output should be "Buzz". If the
number given is a multiple of both 3 and 5, the output should be
"FizzBuzz". If the number is not a multiple of either 3 or 5, the
number should be output on its own.
I wrote this code to solve it (obviously it can be better):
def fizz_buzz(num):
if num % 3 == 0 and num % 5 == 0:
return 'FizzBuzz'
elif num % 3 == 0:
return 'Fizz'
elif num % 5 == 0:
return 'Buzz'
else:
return str(num)
But, once I submitted my solution I was able to see the top solution, which is:
def fizz_buzz(num):
return "Fizz"*(num%3==0) + "Buzz"*(num%5==0) or str(num)
My question is what is the * doing here? Can someone point me to documentation or resources that addresses what this persons code has done? I don't find it super readable but it is apparently the best solution to the problem.

bool in Python is a subclass of int; True has the value 1, False, 0.
Sequences (including str) in Python can be multiplied, to get the sequence repeated that many times, so:
"Fizz"*(num%3==0)
multiplies "Fizz" by 1 (numeric value of True) when num % 3 == 0, producing the original string, and by 0 (numeric value of False) otherwise, producing the empty string.
The same work is done with "Buzz" and concatenated. If both of them produced the empty string (which is falsy), then the or means str(num) is evaluated and returned (Python's or and and don't evaluate to strict True or False, they evaluate to the last item evaluated, and short-circuit, so or always evaluates to the first truthy item, or the last item in the or chain regardless of truthiness).

Firstly, shorter doesn't always mean better. In this case, your solution is fine, and the "top solution" is clever, but needlessly confusing, as you're aware :P
The star is doing string multiplication, and it's exploiting the fact that False == 0 and True == 1. So if num is divisible by 3, you get 'Fizz' once, and if num is divisible by 5, you get 'Buzz' once. Otherwise you get an empty string, '', and because an empty string is falsy, the or clause means it will be replaced by str(num).

The * is string multiplication as usual. It's just multiplying by 0 or 1 based on whether the condition to print the phrase is met. If neither condition is met, it defaults to returning the number, as it should.

I don't know about a resource, but I can tell you what it does. The phrase num%3==0 is a Boolean which is true if the number is divisible by 3. If that's the case, this phrase returns a 1 (or 0 if False). Same for num%5==0 and divisible by 5. This is then being multiplied by the string Fizz and/or Buzz and concatenated together, where multiplying by 1 returns the string and 0 nothing. If neither of those holds, it returns str(num).

I do not know the link to any documentation or resource about it, but I can explain it to you.
Firstly in python str can be multiplied by a number, for instance 'Fizz' * 3 will return 'FizzFizzFizz' or 'Fizz' * 0 will return '', an empty string.
Secondly bool in python can be also recognized as int, i.e. True is 1 and False is 0.
Thirdly in python strings can be added e.g. 'Fizz'+'Buzz' will return 'FizzBuzz'
What the best solution guy does is very simple by num%3==0 he gets bool and just multiplies the str 'Fizz' or 'Buzz' by it and returns.

Related

How to check if a variable is binary in Python

In order to check if a given list is constituted only by 0 and 1 values, I tried to set up a function returning True when the list is binary, while it returns False when not:
My code
def is_binary(y):
for x in y:
if x in [2,3,4,5,6,7,8,9]:
return False
break
else:
return True
Itried it on the following list:
our_list=[1,0,0,0,1,1,0,0,0,0,1,0,1,0,1,1,1]
is_binary(our_list)
Output:
True
But it doesn't work when the variable is not binary.
Any help from your side will be appreciated.
You can also use a list comprehension with the result of checking if each element is binary, and use all to check that all elements are True.
Edit: as #Jonh Coleman suggested, instead of creating a list first and then applying all, we can take advantage of generators, which will only generate values as requested.
We then only need to generate elements until one of them evaluates False. all will take care of that by shortcircuiting and not continuing to request elements to the generator once one of them evaluates False. That will be both more memory and time efficient.
our_list=[1,0,0,0,1,1,0,2,0,0,1,0,1,0,1,1,1]
all(i in [0,1] for i in our_list)
False
def is_binary(arr):
for num in arr:
if num not in [0, 1]:
return False
return True
our_list=[1,0,0,0,1,1,0,0,0,0,1,0,1,0,1,1,1]
print(is_binary(our_list))
I found several mistakes in your code.
Firstly, your code stops when the return statement is executed. So your loop will only be executed once and return the result of the first element in the list.
Secondly, there are two binary numbers, and countless numbers that aren't binary. You're just checking if the numbers are in range(2, 10). When the number is not in that range, take 11 as example, since it is not in range(2, 10), it won't execute the return False statement.
Therefore, rather to check if the number is in countless un-binary numbers, check if the number is not binary.
I would turn around your logic.
def is_binary(y):
for x in y:
if x not in [0,1]:
return False
return True
The root of the problem is that you are returning the result at the first iteration round, because the return statement stops the execution of the function. This also makes your break statement redundant. See https://www.geeksforgeeks.org/python-return-statement/ for more info.
There are two use cases where your original solution works as expected and that is when the list length is one or the error is in the first item of the list y AND the "wrong" value is in the list [0,1, ...9] e.g. y=[0] or y=[1] or y=[1,0] returns True and y=[3] will return False. However, your solution fails, if y=['three'] or y=[13] or y=[0, 0, 3] because it returns True, though it should be False.

Find The Parity Outlier using dictionary {Python}

during the Kata on Codewars called 'Find The Parity Outlier' I faced a problem, and have been trying to solve it using dictionary. I pass almost all tests except 4.
Instruction for the Kata is:
You are given an array (which will have a length of at least 3, but could be very large) containing integers. The array is either entirely comprised of odd integers or entirely comprised of even integers except for a single integer N. Write a method that takes the array as an argument and returns this "outlier" N.
The function is:
def find_outlier(integers):
d = dict()
count = 0
count1 = 0
for i in range(len(integers)):
if integers[i] % 2 != 0 :
d['odd'] = integers[i]
else:
d['even'] = integers[i]
for j in range(len(integers)):
if integers[j] % 2 == 0:
count += 1
else:
count1 += 1
if count > count1:
return d['odd']
return d['even']
Test Results:
2 should equal 1
36 should equal 17
36 should equal -123456789
0 should equal 1
So the question is? Why is it so? Can you help me to sort the problem out? Thanks a lot!
I'm not sure what exactly you're referring to with that list of test results. In general though, your method with the dictionary seems like it might be overcomplicating things a bit as well. You shouldn't need to use a dict, and you shouldn't need two for loops either. Here's an alternative solution to this problem using only list comprehension.
def find_outlier(arr):
# input should be an array-like of integers (and length >= 3) with either only one odd element OR only one even element
odd_mask = [n%2 != 0 for n in arr] # boolean array with True in the location(s) where the elements are odd
even_mask = [n%2 == 0 for n in arr] # boolean array with True in the location(s) where the elements are even
N_odd = sum(odd_mask) # number of odd elements in the input
N_even = sum(even_mask) # number of even elements in the input
if N_even == 1: # if even is the 'outlier'...
return arr[even_mask.index(True)] # return the element of the input array at the index we determined we had an even
elif N_odd == 1: # if odd is the 'outlier'...
return arr[odd_mask.index(True)] # return the element of the input array at the index we determined we had an odd
else: # something has gone wrong or the input did not adhere to the standards set by the problem
return None
And even this is technically not as efficient as it could be. Let me know if you try this and whether it solves whatever issue you were experiencing with expected results.
In your code the final part should not be in the else block, nor even in the for loop:
if count > count1:
return d['odd']
return d['even']
Like this is may give a wrong result. For instance, if the first number in the input is odd, and is the only odd one, then this code will return d['even'] which is obviously wrong.
Place these lines after the loop (with correct indentation) and it should work.
However, this problem can be solved without dictionary or extra lists. Have a go at it.
def find_outlier(integers):
parity = integers[-2] % 2
if integers[-1] % 2 != parity:
if integers[0] % 2 != parity:
return integers[-2]
else:
return integers[-1]
for i in integers:
if i % 2 != parity:
return i

How do boolean operators work in 'if' conditions?

I am currently new to Python and am trying to run a few simple lines of code. I cannot understand how Python is evaluating this syntax after the if statement. Any explanations will be appreciated.
number = int(raw_input("Enter number : "))
if number == (1 or 2 or 3):
print "Match"
else:
print "No match"
Only the integer 1 yield a positive result and any other numbers including 2 and 3 go through the else branch. Can the conditions be stated as the following only?:
if number == 1 or number == 2 or number == 3:
Thank you.
You probably want:
if number in (1, 2, 3):
Python has boolean values, such as True and False, and it also has falsy values, such as any empty list, tuple, or dictionary, an empty string, 0, and None. Truthy values are the opposite of that, namely anything that's defined.
Python's or evaluates and short-circuts on the first element that returns a truthy value.
So, the expression (1 or 2 or 3) is going to return 1.
If you want to compare against all elements, then you're looking for the in keyword:
if number in (1, 2, 3):
# Do logic
The or operator takes two arguments, on its left and right sides, and performs the following logic:
Evaluate the stuff on the left-hand side.
If it is a truthy value (e.g, bool(x) is True, so it's not zero, an empty string, or None), return it and stop.
Otherwise, evaluate the stuff on the right-hand side and return that.
As such, 1 or 2 or 3 is simply 1, so your expression turns into:
if number == (1):
If you actually mean number == 1 or number == 2 or number == 3, or number in (1, 2, 3), you'll need to say that.
(Incidentally: The and operator works the same way, except step 2 returns if the left-hand-side is falsey.)

(Binary) Summing the elements of a list

I need to sum the elements of a list, containing all zeros or ones, so that the result is 1 if there is a 1 in the list, but 0 otherwise.
def binary_search(l, low=0,high=-1):
if not l: return -1
if(high == -1): high = len(l)-1
if low == high:
if l[low] == 1: return low
else: return -1
mid = (low + high)//2
upper = [l[mid:high]]
lower = [l[0:mid-1]]
u = sum(int(x) for x in upper)
lo = sum(int(x) for x in lower)
if u == 1: return binary_search(upper, mid, high)
elif lo == 1: return binary_search(lower, low, mid-1)
return -1
l = [0 for x in range(255)]
l[123] = 1
binary_search(l)
The code I'm using to test
u = sum(int(x) for x in upper)
works fine in the interpreter, but gives me the error
TypeError: int() argument must be a string or a number, not 'list'
I've just started to use python, and can't figure out what's going wrong (the version I've written in c++ doesn't work either).
Does anyone have any pointers?
Also, how would I do the sum so that it is a binary xor, not simply decimal addition?
You don't actually want a sum; you want to know whether upper or lower contains a 1 value. Just take advantage of Python's basic container-type syntax:
if 1 in upper:
# etc
if 1 in lower:
# etc
The reason you're getting the error, by the way, is because you're wrapping upper and lower with an extra nested list when you're trying to split l (rename this variable, by the way!!). You just want to split it like this:
upper = the_list[mid:high]
lower = the_list[:mid-1]
Finally, it's worth noting that your logic is pretty weird. This is not a binary search in the classic sense of the term. It looks like you're implementing "find the index of the first occurrence of 1 in this list". Even ignoring the fact that there's a built-in function to do this already, you would be much better served by just iterating through the whole list until you find a 1. Right now, you've got O(nlogn) time complexity (plus a bunch of extra one-off loops), which is pretty silly considering the output can be replicated in O(n) time by:
def first_one(the_list):
for i in range(len(the_list)):
if the_list[i] == 1:
return i
return -1
Or of course even more simply by using the built-in function index:
def first_one(the_list):
try:
return the_list.index(1)
except ValueError:
return -1
I need to sum the elements of a list, containing all zeros or ones, so that the result is 1 if there is a 1 in the list, but 0 otherwise.
What's wrong with
int(1 in l)
I need to sum the elements of a list, containing all zeros or ones, so that the result is 1 if there is a 1 in the list, but 0 otherwise.
No need to sum the whole list; you can stop at the first 1. Simply use any(). It will return True if there is at least one truthy value in the container and False otherwise, and it short-circuits (i.e. if a truthy value is found early in the list, it doesn't scan the rest). Conveniently, 1 is truthy and 0 is not.
True and False work as 1 and 0 in an arithmetic context (Booleans are a subclass of integers), but if you want specifically 1 and 0, just wrap any() in int().
Stop making nested lists.
upper = l[mid:high]
lower = l[0:mid-1]

How to write more Pythonic Code

I started learning python today from the tutorial on the official site.
When reading about filter(function, sequence) i thought of making a function that returns if a number is prime to use it with the filter.
notDividedBy = [2,3,4,5,6,7,8,9]
def prime(num):
"""True if num is prime, false otherwise"""
copy = notDividedBy[:]
check = True
if num in copy:
copy.remove(num)
for x in copy:
if num % x == 0:
check = False
break
return check
The above code works in the shell.
My question is: Since i feel like although a solution, it is not the most elegant one, can anyone transform this code to something more python-like?(better structure? less lines?)
I believe it would help me for better understanding of the basics of the language.
The thing is, don't use any imports or anything, just simple staff.
Creating many many copies of lists is not a particularly efficient way of doing things. Instead use the xrange() (Python 2.x) or range() (Python 3) iterator. Here's one (naive) way you could implement a primality test:
from math import sqrt
def isPrime(n):
if n < 2: return False
if n == 2: return True
if not n % 2: return False #test if n is even
#we've already remove all the even numbers, no need to test for 2
#we only need to test up to sqrt(n), because any composite numbers can be
# factored into 2 values, at least one of which is < sqrt(n)
for i in xrange(3, int(sqrt(n)) + 1, 2):
if not n % i:
return False
return True
How about this one:
def is_prime(num):
return not any(num%i == 0 for i in xrange(2,num/2+1))
for i in xrange(10):
print i, is_prime(i)
Explanation
start with:
(num%i==0 for i in xrange(2,num/2+1))
This is a generator expression. I could have made it a list comprehension:
[num%i==0 for i in xrange(2,num/2+1)]
The list comprehension is equivalent to:
ll=[]
for i in xrange(2,num/2+1):
ll.append(num%i==0)
The difference between the generator and the list comprehension is that the generator only gives up it's elements as you iterate over it -- whereas the list comprehension calculates all the values up front. Anyway, from the above code, you can see that the expression generates a sequence of True's and False's. True if the number can be divided by i and False otherwise. If we generate a sequence of all False numbers, we know we have a prime.
The next trick is the any built in function. It basically searches through an iterable and checks if any of the values is True. As soon as it hits a True, it returns True. If it gets to the end of the iterable, it returns False. So, if the entire sequence is False (a prime number) then any will return False, otherwise it returns True. This would be perfect for a not_prime function, but our function is is_prime, so we just need to invert that result using the not operator.
The benefit of using the generator expression is that it is nice and concise, but also that it allows any to return before checking every value which means that as soon as it finds a number that divides num, it returns instead of generating all num/2 numbers.
Anyway, I hope this explanation is helpful. If not, feel free to leave a comment and I'll try to explain better.
One thing off the bat, if you are going to implement prime testing in this fashion, there's no reason to use an auxillary array
def prime(num):
"""True if num is prime, false otherwise"""
check = True
#if num in copy:
# copy.remove(num)
for x in range(2,x-1):
if num % x == 0:
check = False
break
return check
Here's a 2 liner using filter().
def prime(num):
"""True if num is prime, false otherwise"""
if num < 2:
return False
return len(filter(lambda x: num % x == 0, range(2, num))) == 0

Categories