I have been part of a code review in which I have a condition such as this one (Python code):
my_list = <whatever list>
if len(my_list) <= 0:
do_something()
I have been pointed out that the condition should be == instead of <= as a list cannot have a negative length. I have no issues with it in this case, but I wanted to know if there is really any drawbacks on using <=.
Here is my rationale on why to use <=:
The actual condition that I want to check is not len(my_list) > 0 which is equivalent to len(my_list) <= 0, so that is correct for sure.
As explained in point 1, len(my_list) == 0 alone is not equivalent to the original condition. It is in this case, but it is not trivial why it is this way. len() returns the value returned by __len__() which may be an arbitrary. It only happens to be equivalent because the len() implementation performs some validations on the value. One could argue that len() implementation may change in the future, so the equivalence would not hold anymore.
I feel len(my_list) <= 0 would be safer in general than using len(my_list) == 0 plus an assumption that might not hold in some edge or unexpected cases.
So, are my arguments correct? Do you know any drawbacks of using len(my_list) <= 0?
Sorry if this seems like a non-relevant question, but this have been raised to me more than once on code reviews so wanted to understand if there is something I am missing.
So, after some more digging I found this in the python PEP 8 programming recommendations (https://peps.python.org/pep-0008/#programming-recommendations)
# Not recommended
my_list = []
if not len(my_list):
print('List is empty!')
# Recommended
my_list = []
if not my_list:
print('List is empty!')
so now you know the most pythonic way to check if a list is empty!
After testing a bit, I found this works even for custom classes! But my test might be incomplete.
class Test:
def __init__(self, len):
self.len = len
def __len__(self):
return self.len
t1 = Test(3)
if len(t1):
print("len1 > 0")
if t1:
print("len2 > 0")
t1 = Test(0)
if len(t1):
print("len3 > 0")
if t1:
print("len4 > 0")
t1 = Test(-1)
if len(t1):
print("len5 > 0")
prints
len1 > 0
len2 > 0
Exception has occurred: ValueError
__len__() should return >= 0
Related
Here is my code:
def isSorted(arr):
n = len(arr)
if n == 0 or n == 1:
return True
elif arr[n-2] > arr[n-1]:
return False
return isSorted(arr[0:n-1])
arr = [1,9,9,4,5]
isSorted(arr)
if isSorted:
print("yes")
else:
print("no")
Answer is always yes, even if the array is unsorted. Can anybody please explain what mistake am I making?
Your code was fine with the exception of the error noted in the comments: you were ignoring the return value of isSorted(arr) by just checking if isSorted:. isSorted is a callable (and not None or zero or an empty string, etc.) and thus evaluates to True.
Here's a slight modification to your code, using negative indices to count back from the end of the array. For example, -2 is the same as n-2 when n is the length of the array.
I've also thrown in a little syntactic sugar at the end, using python's ternary operator.
def isSorted(arr):
if len(arr) < 2:
return True
elif arr[-2] > arr[-1]:
return False
return isSorted(arr[:-1])
arr = [1,9,9,4,5]
print("yes" if isSorted(arr) else "no")
A recursive approach can be implemented like this:
def isSorted(lst):
if len(lst) < 2:
return True
return False if lst[1] < lst[0] else isSorted(lst[1:])
However, it's going to be slow compared with:
lst == sorted(lst)
...due to the efficient implementation of the sort() function
As others have pointed out, there are non-recursive solutions which will outperform recursive implementations.
Having said that, if you insist on using recursion you should seek approaches that will not give you a stack overflow with larger arrays. Solutions which whittle the size of the problem down one element at a time will cause stack overflow on arrays that are larger than ~1000 elements. To avoid this, use an approach which cuts the problem size in half at each level of the recursion. The following algorithm does this:
def isSorted(ary):
if len(ary) < 2:
return True
mid = len(ary) // 2
return (ary[mid-1] <= ary[mid]) and isSorted(ary[:mid]) and isSorted(ary[mid:])
The logic is:
Arrays of length less than 2 are trivially considered sorted, so return True if this is the case;
Otherwise, find the mid-range value which will split the array into two sub-arrays which differ in length by no more than one;
The array is sorted if and only if the last element of the first sub-array is ≤ the first element of the second subarray, and both sub-arrays are sorted.
Since and is used, short-circuiting can occur. The recursive stack size is O(log(len(ary)).
As Mark pointed out, the reason you always get "yes" is because isSorted, without an argument, is a reference to the function and is considered "truthy" in python.
def isSorted(arr):
n = len(arr)
if n == 0 or n == 1:
return True
elif arr[n-2] > arr[n-1]:
return False
return isSorted(arr[0:n-1])
arr = [1,9,9,4,5]
#isSorted(arr)
#You should use "if isSorted(arr):" instead of "isSorted(arr)".
if isSorted(arr):
print("yes")
else:
print("no")
Simply add the parameters to the function in the if statement:
if isSorted(arr):
print("yes")
else
print("no")
I am using Codelite to train myself as I just started learning Python. In the excercise "Smallest Positive Number", I used the code below and some test cases passed, but most of them not. How can I improve my code? (test cases like [1,2,3,4] and repetitive numbers like [5,6,2,4,6].
A = [2,1,-2,5,-6]
m = max(A)
temp = []
index = []
result = []
if m < 1:
print ("1")
elif len(A) == 1:
if A[0] == 1:
print("2")
else:
print("1")
else:
for i in range(len(A)):
if A[i] > 0:
temp.append(A[i])
temp.sort()
print(temp)
for j in range(len(temp)):
index.append(j+1)
if temp[j] != index[j]:
result.append(index[j])
print(min(result))
Thanks!
First you would want to store the smallest positive number. Start it at the first element of the list.
small_positive = A[0]
You would then want to iterate through the input
for number in A:
Then check if the number is positive. The easiest way is to see if it is greater then zero.
if number > 0:
After, check if the number is smaller then the current smallest. If so, overwrite it.
if number < small_positive:
small_positive = number
Lastly, you need to check if there were all negatives in the list. If small_positive is negative after the loop, there were no positives in the list.
if small_positive < 0:
return None
This is the easiest way to understand the solution, but there are many built in functions which are much simpler then this.
A = [2,-2,5,-6]
def small(a,b):
if a >= b: return b
else: return a
positive_list = [ele for ele in A if ele > 0]
res = min(positive_list)
if len(positive_list) == 0:
print("no positive number.")
else:
for i in range(len(positive_list)):
res = small(res, positive_list[i])
print(f"Smallest Positive number : {res}")
by this, yon can easily get a result.
For a start, this wouldn't work for the list [-42]:
m = max(A)
if m < 1:
print ("1")
There's no 1 anywhere in that given list so it should not be reported as the solution. Python would usually give you None in that scenario but the puzzle you've been set may have other ideas. It also wouldn't work with the list [] as max() will complain bitterly about the empty list.
You may think that one or both of those cases are unlikely but that's one of the things that makes the difference between a good coder and a not-so-good one. The former assume their users and testers are psychotic geniuses who know how to break code with near-zero effort :-)
To code it up by yourself, you can use the following pseudo-code(1) as a basis:
num_list = (some list of numbers)
min_pos = nothing
for num in num_list:
if num > 0 and (min_pos is nothing or num < min_pos): # >= ?
min_pos = num
print(min_pos)
Of course, if you're learning Python rather than learning programming, you should probably just use the facilities of the language, things will be much easier:
pos_list = [item for item in the_list if item > 0] # >= ?
min_pos = min(pos_list) if len(pos_list) > 0 else None
print(min_pos)
And, if you consider zero to be positive, make sure you use >= 0 instead of > 0, in both code blocks above where indicated.
Both of these snippets will successfully handle lists with no positive numbers, giving you a sentinel value (Python None or pseudo-code nothing).
(1) Yes, it looks a bit like Python but that's only because Python is the ultimate pseudo-code language :-)
I am reading Grokking Algorithms, which seems to be a highly recommended book. I am looking at the first algorithm for "binary search" and the guy uses two "ifs" instead of a "if" and "elif". Would using two "ifs" be better or faster?
def binary_search(list, item):
low = 0
high = len(list) - 1
while low <= high:
mid = (low + high)
guess = list[mid]
if guess == item:
return mid
if guess > item:
high = mid - 1
else:
low = mid + 1
return None
my_list = [1,3,5,7,9]
example1
>>> def foo():
a = 10
if a == 10:
print("condition1")
elif a == 10:
print("condition2")
else:
print(0)
>>> foo()
condition1
>>>
elif is guaranteed not to run when if is true.
example2
def foo():
a = 10
if a == 10:
print("condition1")
if a == 10:
print("condition2")
else:
print(0)
>>> foo()
condition1
condition2
>>>
example3
modify if statement of example2.
if a == 10:
print("condition1")
return a
output
>>> foo()
condition1
10
So, in your case adding a return in first if statement has similar operation like an if-elif block. the (return a) is preventing the second if statement to be executed in example3.
Multiple IFs
You use multiple ifs, when you want to accomplish different tasks that are independent of each other. And the execution of one of the tasks doesn't effect the execution of others.
Let's look at an example:
if primeMember:
makeDeliveryFree()
if validDiscountCoupon:
giveDiscount(couponCode)
if customersBirthday:
giveBirthdayDiscount()
So, in the above example we have different tasks that we want to perform under different conditions and the tasks are independent of each other. Making the delivery free doesn't effect the discount in any manner.
With multiple ifs, it could be possible that the statements within all the ifs get executed and on the other hand it could also be possible that none of the statements within the ifs get executed.
IF, ELIF, ELSE Chain
On the other hand we would use an if, elif, else chain, when we want to accomplish a particular task but we want to accomplish that differently under different conditions.
Let's look at an example:
if hasBalanceInWallet:
setPaymentMode("wallet")
elif hasCreditCardSaved:
setPaymentMode("credit-card")
else
showPaymentModeSelectorDialog()
So, in the above example the task that we're trying to accomplish is that of setting the mode of payment and we need to set it differently under different scenarios but we only want to set it once (i.e. we want only one of the branches to run).
As other answers mentioned, an elif is not necessary after an if that does a return. But they didn't cover the performance impact. I thought it would be interesting to test.
Turns out, it can be a tad faster to use if/elif/else instead of separate if's.
from timeit import timeit
import random
def count_items_ifelifelse(items, threshold):
below, on, above = 0, 0, 0
for item in items:
if item > threshold:
above += 1
elif item < threshold:
below += 1
else:
on += 1
return below, on, above
def count_items_ififif(items, threshold):
below, on, above = 0, 0, 0
for item in items:
if item > threshold:
above += 1
if item < threshold:
below += 1
if item == threshold:
on += 1
return below, on, above
def generate_items_and_threshold(count=100_000):
"""List of reproducible random numbers to test with. Set threshold at half"""
items = list(range(count))
random.Random(123).shuffle(items)
threshold = count // 2
return items, threshold
def run_test():
t1 = timeit(
"count_items_ifelifelse(i, t)",
setup="from __main__ import count_items_ifelifelse, generate_items_and_threshold; i, t = generate_items_and_threshold()",
number=1000,
)
print("count_items_ifelifelse: {:.2f}".format(t1))
t2 = timeit(
"count_items_ififif(i, t)",
setup="from __main__ import count_items_ififif, generate_items_and_threshold; i, t = generate_items_and_threshold()",
number=1000,
)
print("count_items_ififif: {:.2f}".format(t2))
if __name__ == "__main__":
run_test()
This outputs (Python 3.8.2, Windows):
count_items_ifelifelse: 6.69
count_items_ififif: 8.71
Roughly 20% faster, because of more unnecessary if evaluations each loop. The other side of it is that for if/elif/else the performance will vary based on how the data is sorted, and whether it checks the most occurring condition first.
For an interview question I made a roman to integer converter:
def romanToInt(self, s):
"""
:type s: str
:rtype: int
"""
mapping = {"I": 1, "V":5, "X":10, "L":50, "C":100, "D":500, "M":1000}
numeral_list = list(s)
num_list = [[mapping[i], -1] for i in numeral_list]
count = 0
i = 0
while(i < len(num_list) - 1):
if num_list[i] < num_list[i + 1]:
count += num_list[i + 1][0] - num_list[i][0]
num_list[i+1][1] = 1
num_list[i][1] = 1
i += 2
else:
count += num_list[i][0]
num_list[i][1] = 1
i += 1
if num_list[-1][1] == -1:
count += num_list[-1][0]
return count
As you can see I sometimes miss the last digit because I didn't want to get an index error. To avoid that I added an extra attribute that would check if the last element was checked or not (in cases where s[len(s)-2] < s[len(s)-1], s[len(s)-1] is checked, but if s[len(s)-2] > s[len(s)-1] then s[len(s)-1] is not checked.
Having an extra check and using extra space just for one element is highly erroneous. Where am I going wrong in my logic?
EDIT: Here was my code before
def romanToInt(self, s):
"""
:type s: str
:rtype: int
"""
mapping = {"I": 1, "V":5, "X":10, "L":50, "C":100, "D":500, "M":1000}
numeral_list = list(s)
num_list = [mapping[i] for i in numeral_list]
count = 0
i = 0
while(i < len(num_list)-1):
if num_list[i] < num_list[i + 1]:
count += num_list[i + 1] - num_list[i]
i += 2
else:
count += num_list[i]
i += 1
return count
It failed on several test cases since it did not count the last digit
What they are looking for before anything else is whether your code is easily readable and maintainable. That's more important than being right, in my opinion, although the interviewer may disagree -- so make it also correct.
Check for invalid inputs. What if they give a string like 'VXQIII'? Do you want to enforce any rules or are you okay with giving 'VVVVV' = 25?
Throw in a unit test or test function for bonus points.
You invent a complicated structure with mysterious code numbers instead of using a clearly named variable like 'has_been_counted'. This makes your code hard to read. Therefore all your code will be hard for other programmers to read and maintain. And when I say other programmers, I mean you, next week, when you can't remember why you did that.
Additionally, that seen flag is unnecessary. You already have an array index telling you what you have and have not seen.
Python specific:
For an interview use pep-8. You can figure out how strict they are about style later, but python people can be pickier about that than most languages.
Self is unused, and it's not shown as being a class member anyway. "print romanToInt('XCIV')" will raise an error.
Speaking of errors, Python people may appreciate catching invalid characters with a try..except around the mapping lookup, and then reraise as ValueError('Not a valid roman numeral'). Maybe a python person can comment on what exception type to use for that, but I think ValueError is the right one.
converting s to a list of characters is unnecessary. You can already iterate a string as a list of characters.
for letter in 'XCIV':
print letter
X
C
I
V
How can I tell if a list (or iterable) of numbers all have the same sign?
Here's my first (naive) draft:
def all_same_sign(list):
negative_count = 0
for x in list:
if x < 0:
negative_count += 1
return negative_count == 0 or negative_count == len(list)
Is there a more pythonic and/or correct way of doing this? First thing that comes to mind is to stop iterating once you have opposite signs.
Update
I like the answers so far although I wonder about performance. I'm not a performance junkie but I think when dealing with lists it's reasonable to consider the performance. For my particular use-case I don't think it will be a big deal but for completeness of this question I think it's good to address it. My understanding is the min and max functions have O(n) performance. The two suggested answers so far have O(2n) performance whereas my above routine adding a short circuit to quit once an opposite sign is detected will have at worst O(n) performance. Thoughts?
You can make use of all function: -
>>> x = [1, 2, 3, 4, 5]
>>> all(item >= 0 for item in x) or all(item < 0 for item in x)
True
Don't know whether it's the most pythonic way.
How about:
same_sign = not min(l) < 0 < max(l)
Basically, this checks whether the smallest element of l and the largest element straddle zero.
This doesn't short-circuit, but does avoid Python loops. Only benchmarking can tell whether this is a good tradeoff for your data (and whether the performance of this piece even matters).
Instead of all you could use any, as it short-circuits on the first true item as well:
same = lambda s: any(i >= 0 for i in s) ^ any(i < 0 for i in s)
Similarly to using all, you can use any, which has the benefit of better performance, as it will break the loop on the first occurrence of different sign:
def all_same_sign(lst):
if lst[0] >= 0:
return not any(i < 0 for i in lst)
else:
return not any(i >= 0 for i in lst)
It would be a little tricky if you want to consider 0, as belonging to both groups:
def all_same_sign(lst):
first = 0
i = 0
while first == 0:
first = lst[i]
i += 1
if first > 0:
return not any(i < 0 for i in lst)
else:
return not any(i > 0 for i in lst)
In any case, you iterate the list once instead of twice as in other answers. Your code has the drawback of iterating the loop in Python, which is much less efficient than using built-in functions.