How to write more Pythonic Code - python

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

Related

Check if an array is sorted or not using recursion

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")

Recursive function without loops or variables

I am learning coding by myself. I am taking learning recursive function from online resources, and as an exercise, I came across this one exercise where I am tasked to create a recursive function to see if numbers in the list are in order. The exercise says not to use loops and not to use variables, so I can learn recursive function better for the future.
def is_increasing(lst):
if not (lst[0]) < lst[1]:
return False
else:
return is_increasing(lst[1:])
when I input is_increasing([1,2,12,24]) in the console.
The errors says:
IndexError:list index out of range
What am I doing wrong that is showing this error?
You need a stop condition (or base case) for every recursive function, otherwise it won't stop: in your case, the stop condition is if the list contains one elements or less, it's always sorted:
def is_increasing(lst):
if len(lst) <= 1: return True # Base case
if not (lst[0]) < lst[1]:
return False
else:
return is_increasing(lst[1:])
You didn't take the recursion far enough. You are assuming that a list will have at least two elements, but that's not true for empty and singleton lists. (Plus, at no point did you ever attempt to return True.)
The true base case is a list with fewer than 2 items, which is trivially sorted. Otherwise, you can check whether the first 2 elements are in order, then recursively check the rest of the list.
def is_increasing(lst):
if len(lst) < 2:
return True
else:
return lst[0] < lst[1] and is_increasing(lst[1:])
When implementing a recursive function, there is always one special case which has to be handled differently: how does the recursion stop?
The implementation show here correctly reduces the problem of solving an input of size N to a problem of size N-1:
def is_increasing(lst):
if not (lst[0]) < lst[1]:
return False
else:
return is_increasing(lst[1:])
What is missing is handling the situation where the size is 0 or 1. Therefore:
def is_increasing(lst):
if len(lst) < 2:
return True # That is always sorted
# BTW this is correct only if it is supposed to be descending,
# oherwise it should be lst[0] > lst[1]
if not lst[0] < lst[1]:
return False
else:
return is_increasing(lst[1:])
Your function never returns True. That's not directly the cause of the error, but that's related to it.
For a recursive function that returns a bool, you need two base cases, not just one: one that returns True and one that returns False.
So when should it return True? What is the relevant base case? It is when the list is too small to fail: a list that is of length 1 or 0 is always increasing, technically. And in that case, there is no lst[1], which causes the IndexError. So this is the solution:
def is_increasing(lst):
if len(lst) < 2:
return True
elif lst[0] >= lst[1]:
return False
else:
return is_increasing(lst[1:])
(You also had a mistake where you compare not lst[0] with lst[1], I fixed that for you ;)

What’s wrong with my recursion for finding primes? (Python)

I’m trying to create a program that lists all the primes below an inputted number, and I came up with the code:
def primes():
num = 20
numlist = list(range(1,num+1))
i = len(numlist)
for j in numlist[2:]:
ans = divisible(j,i)
if ans:
numlist.remove(j)
print(numlist)
def divisible(m,n):
if m!=n and m%n==0:
return True
elif n == 1:
return False
else:
divisible(m, n-1)
primes()
(I used an in-browser IDE so the num part was a proxy for the input.)
My idea was to create a separate function divisible() that when inputted two ints, m and n, would check if n divides m. I'm not sure if I was right in my recursion, but I wrote divisible(m,n-1) the idea was that it would iterate through all the integers from n downward and it would return True if any n divided m, or False if it reached 1.
In the main code, m iterated through all the numbers in a list, and n is the total number of elements in the same list. I put the print(numlist) inside the if statement as an error check. The problem I’m having is nothing is printing. The code returned literally nothing. Is there something I’ve missed in how recursion works here?
There's a lot wrong here:
You've made a common beginner's recursion error in that you have a recursive function that returns a value, but when you call it recursively, you ignore the returned value. You need to deal with it or pass it along.
It seems like this modulus is backward:
if ... and m%n==0:
Maybe it should be:
if ... and n % m == 0:
You're code doesn't appear to be calculating primes. It's looks like it's calculating relative primes to n.
You start your list of numbers at 1:
numlist = list(range(1,num+1))
But you start testing at index 2:
for j in numlist[2:]:
Which is the number 3 and you never check the divisibility of the number 2.
Even with all these fixes, I don't feel your algorithm will work.
Your divisible function doesn't return anything if it falls into else part. change it as
def divisible(m, n):
if m!=m and m%n==0:
return True
elif n==1 :
return False
else:
return divisible(m,n-1)
This should work

Getting First Prime in a List of Random Numbers

I was playing around with the Python shell and I have what I believe is an extremely naive implementation of a function that simply returns the first prime number in a list of 100 randomly generated numbers (whose values are between 0 and 99, inclusive). Code below:
>>> def is_prime(n):
if n < 2:
return False
elif n == 2:
return True
for i in range(2, n):
if n % i == 0:
return False
return True
>>> from random import randint
>>> numbers = []
>>> for i in range(0, 100):
numbers.append(randint(0, 99))
>>> def get_first_prime(values):
temp = []
for i in values:
if is_prime(i):
temp.append(i)
return temp[0]
>>> get_first_prime(numbers)
I want this function to strictly return only the first prime number. My implementation uses a helper list to cache all primes and then simply return the element at the first index. It works, but I'm not convinced it's a good one. I'm sure there is a more efficient way of doing this that does not require scanning through the entire list, but I can't seem to think of one yet.
What are some better alternatives?
def get_first_prime(values):
for i in values:
if is_prime(i):
return i
This way you don't keep searching once you find a prime. The function implicitly returns None if no prime is found.
Yes, you do not even need to generate a list of all the random numbers, you can test each one as they are generated, and return as soon as you've found one.
from random import randint
import math
def is_prime(n):
if n < 2:
return False
elif n == 2:
return True
for i in range(2, int(math.sqrt(n) + 1)):
if n % i == 0:
return False
return True
def get_first_prime(number):
for i in range(number + 1):
n = randint(0, 99)
if is_prime(n):
return n
get_first_prime(100)
You are right. Your code not only has more time complexity (running through all the list elements even after finding the first prime) and space complexity (temp list to hold all the primes) but also stands a risk of throwing IndexError when there are no prime numbers in the list.
So you could mend it in the following manner:
def get_first_prime(values):
for i in values:
if is_prime(i):
return i
Note that when there is no prime in the input the return statement would never get executed that means there is no explicit return; in which case Python returns None. So, if you choose to, you can return the value of your choice at the end of the function outside for loop to indicate 'not found'.
def get_first_prime(values):
for i in values:
if is_prime(i):
return i
return -1
Pythonic way of dealing with this is to raise an Exception and let the caller of the function deal with the exception.
prime = next(filter(isprime, numbers))
See find first element in a sequence that matches a predicate.

Can I use a list while forming it in a list comprehension?

For example, the below code
primeList = []
for val in range(2, num):
if not any(val % i == 0 for i in primeList):
primeList.append(val)
How can I turn this exact piece of code into list comprehension?
No, you can't, because the list doesn't exist as a Python object until the comprehension is finished iterating. You can't reference an object that doesn't exist. Honestly, I would just leave this as a for loop - list comprehensions aren't a magic bullet to replace all list-constructing loops.
However... you can get tricky and use a generator, which is only evaluated on demand. Combining this with the extend() method of a list, which adds elements to the list as it obtains them (in my tests, anyway), you can use the current contents of the list you're extending as you extend it.
# make sure to set `num`
plist = []
plist.extend(c for c in range(2, num) if not any(c % p == 0 for p in plist))
Fair warning: as far as I know, the fact that extend() adds elements to the list as it produces them is not part of the specification, so whether this bit of code works could be implementation-dependent. But I tested it in Python 2.7 and 3.4 and got a list of primes each time.
Actually, if you really really want to, you can create an identical copy of the list within the list comprehension and refer to that...
primeListCopy = []
primeList = [primeListCopy.append(val) or val for val in range(2, num)
if not any(val % i == 0 for i in primeListCopy)]
This uses the fact that primeListCopy.append(val) or val evaluates to val, because assignment to list returns None and or evaluates to the right side value.
This is definitely worse performance-wise than a simple for-loop. I wrote this in response to OP's question "what could be the closest mimic when i had nothing but list comprehension. ( this is not a development code, just my experiments)"
That said, the additional work only adds O(n) of work so doesn't actually increase the algorithmic complexity of the loop. It's conceivable (though not very likely) that the list comprehension optimization will make this faster than the original for-loop.
#!/usr/bin/python
from __future__ import print_function
import math
def is_prime(x):
if (x == 0 or x == 1 or x < 0):
return False
for i in range(2, int(math.sqrt(x)) + 1):
if (not (x % i)):
return False
return True
def filter_primes(max):
return [val for val in range(2, max) if is_prime(val)]
def main():
primes = filter_primes(20)
print(primes)
if __name__ == "__main__":
main()
The code above starts with defining a function called is_prime(x) which returns True if x is a prime number, False otherwise. Then a function called filter_primes(max) uses is_prime with list comprehension to filter the prime numbers into an array that's returned. The maximum number of the range is specified via max. The main function just invokes the API to test it out.
EDIT:
But maybe I've misunderstood what you meant by "turn this exact piece of code into list comprehension". Depending on what you really want to do, using a generator is a great idea for dynamic purposes.

Categories