I'm just starting out in Python and I can't seem to work this practice question out.
Write a function that takes in a collection and returns a list of elements of all values that are odd. You should ignore any non-numeric elements in the collection.
Required Signature: def odd_numbers1(x):
for example, calling odd_numbers1([1,2,3,3,'3']) will return [1,3,3]
for example, calling odd_numbers1([False,True,3,5.0,7.5]) will return [True,3, 5.0]
This is what I have so far but I hit an error when I reach '3'.
for i in x:
if x %2 == 0 or type(x) != int:
None
else:
print(x)
x = [1,2,3,3,'3']
odd_numbers1([1,2,3,3,'3'])```
if x %2 == 0 or type(x) != int will be evaluated from left to right. If you have a string, '3', then it will check the modulus before checking if the type is int. You should reverse the order of checking and can also use isinstance: if not isinstance(x, int) or x % 2 == 0.
This will work because of short circuit evaluation.
#chrispresso is exactly right, your checking the modulus before you know if its an int so you need to reverse that logic. Personally I would change the if statement to say. If it is an int, and its not divisable by two then print. That to me looks cleaner and there is no need for an else part
def odd_numbers1(x):
for i in x:
if isinstance(i, int) and i % 2 != 0:
print(i)
odd_numbers1([1, 2, 3, 3, '3'])
OUTPUT
1
3
3
The question speaks of functions and returns, so just printing out the list might not be a correct answer. In addition to #Chrispresso's answer, the simplest way would be something like so:
def odd_numbers1(x):
retval = []
for i in x:
if type(x) != int or x%2==0:
continue
else:
retval.append(x)
return retval
List comprehensions are a great feature of Python that could also really help you out here.
Related
This question already has answers here:
What's the canonical way to check for type in Python?
(15 answers)
Why does "a == x or y or z" always evaluate to True? How can I compare "a" to all of those?
(8 answers)
Closed 6 months ago.
I have two problems that are getting errors that I would like to understand. They are likely for similar reasons, hence why I'm grouping them together in this post. I am here to learn how I can understand solving errors like these!
First, one function aims to find even numbers within a list that contains ints, floats, and strings. For some reason, I get an error that says not all of my arguments were able to convert during string formatting.
Here is my code:
def recEvenNumbers(lst):
'return a count of all the even numbers(ints and floats) in the list'
evens = 0
if lst == []:
return
else:
if type(lst[0])== int or float:
if ((lst[0]%2*10))==0:
evens = evens+1
return recEvenNumbers(lst[1:], evens + 1)
I believe I have this whole function down except this one error. Why is this happening and how can I prevent this in the future?
Another error I am getting is for a different function. Here is the code for that one:
def recMerge(a,b):
'Merge two strings together recursivly'
if len(a)==0:
return
elif len(b)==0:
return
else:
if type(a) == str:
if type(b) == str:
return a[0] + b[0] + recMerge(a[1:], b[1:])
The aim of this function is to merge two strings to create one string that has characters that alternate between each of the two strings. Not really important to my question though, I just want to know why I might be getting a TypeError here. This is what it tells me:
File "C:/Users/1734/py.py", line 59, in recMerge
return a[0] + b[0] + recMerge(a[1:], b[1:])
TypeError: can only concatenate str (not "NoneType") to str
>>>
Why is this happening? I assumed my if type(a) and if type(b) were supposed to handle this. Why can't I use those in this scenario? Or am I just misusing them?
Also, are these two errors related? I know that question may seem strange but if there's a certain element of these questions that I am struggling to understand, I would like to pinpoint what it is and why I am misunderstanding it.
Thanks for your time!
You've committed one of the classic blunders. This statement does not do what you think it does:
if type(lst[0])== int or float:
This is parsed by Python as:
if (type(lst[0])== int) or (float):
And since "float" is always true, you will always take the if here. And when lst[0] is a string, the '%' operator is the string formatting operator. You want:
if type(lst[0]) in (int, float):
or even
if isinstance(lst[0],int) or isinstance(lst[0],float):
The problem with your second function is that it returns None rather than an empty string for the base case where the input is empty. Since your recursion works by adding onto the result of the recursive call, the very last recursive call must be something you can add to.
def recMerge(a: str, b: str) -> str:
"""Merge two strings together recursively. If the strings are of
unequal length, the remainder of the longer string is dropped."""
# Type check (not necessary if you type-annotate and mypy your code!)
if not isinstance(a, str) or not isinstance(b, str):
raise TypeError("both parameters must be strings!")
# Base case: if one param is empty, there's nothing to merge, so return ""
if not a or not b:
return ""
# Recurse with a smaller slice of both parameters.
return a[0] + b[0] + recMerge(a[1:], b[1:])
For the first recursive function, this is the correct code
def recEvenNumbers(lst, evens):
if not lst:
return evens
if type(lst[0]) == int:
if(lst[0]%2 == 0):
return recEvenNumbers(lst[1:], evens + 1)
else:
return recEvenNumbers(lst[1:], evens)
else:
return recEvenNumbers(lst[1:], evens)
x = recEvenNumbers([1, 2, 3, 4, 5.3, 7, 8, "h", 46, "32"], 0)
print(x)
It is important to return when you're writing a recursive function, otherwise, you get "None". When you're calling to a function make sure to add every parameter ex:(last, evens).
I know everyone else is commenting your first problem, but it works if you work it right. That said, you should handle the string first and add evens as a None parameter to check on instance for the first pass.
Here's a working function:
def recEvenNumbers(lst, evens = None):
'return a count of all the even numbers(ints and floats) in the list'
if evens is None:
evens = [0]
if lst:
# To keep things organized, extract first item
# into variable and the rest into a smaller list with
# star selection
item, *lst = lst
# Check for string first
# Pass rest of process if detected.
if type(item) == str:
pass
# Continue as you were before
elif type(item) == int or float:
if item % 2 * 10 == 0:
evens[0] += 1
# Recurse with appropriate params.
return recEvenNumbers(lst, evens)
# Return result.
return evens[0]
Run the function:
test_list = [1, 2.2, 4, "string"]
recEvenNumbers(test_list)
Output:
1
Part 2:
# Function with commentary.
def recMerge(a, b):
'Merge two strings together recursivly'
# Check types
if type(a) == type(b) == str:
# Run until b is empty.
if len(b) > 0:
# Deconstructing into single and rest of items with asterisk.
aa, *a_rest = a
bb, *b_rest = b
# New `a` value that prepends the rest of a in
# front of the concatenation of the current a and b characters.
# Since a_rest is a list, we should "collect" the items back into
# a proper string format.
a = "".join(a_rest) + aa + bb
# Return the new a with another concatenated list for the
# remaining b items.
return recMerge(a, "".join(b_rest))
# Return a. Ensure that it is a string.
return "".join(a)
Run test case:
A = "abc"
B = "def"
recMerge(A, B)
Output:
'adbecf'
here is my working code.
def recEvenNumbers(lst):
'return a count of all the even numbers(ints and floats) in the list'
evens = 0
if lst == []:
return 0
elif type(lst[0]) == int or type(lst[0]) == float:
if ((lst[0]%2))==0:
evens = evens+1
return recEvenNumbers(lst[1:]) + evens
def recMerge(a,b):
'Merge two strings together recursivly'
if type(a) != str or type(b) != str:
return None
#recMerge ((a[1:]), (b[1:]))
if a == "":
return b
elif b == "":
return a
else:
return a[0] + b[0] + recMerge(a[1:], b[1:])
def nested_count(l : 'any nested list of int', a : int) -> int:
c = 0
while len(l) != 0:
for x in l:
if type(x) == int:
if x == a:
c = c + 1
l.remove(x)
nested_count(l,a)
else:
continue
elif type(x) == list:
nested_count(x,a)
return c
this function passed a nested list of ints and a single int as arguments; it returns the number of times the single int argument appears in the nested list argument, for example:
nested_count( [[1,2,[4,[1],8],[1,3,2]],[1,1]], 1 )
returns 5
I am not sure why my function does not work
can someone tell me how to fix it? many thanks.
You are not adding nested_count results to c:
def nested_count(lst, l):
c = 0
for i in lst:
if i == l:
c += 1
elif type(i) == list:
c += nested_count(i, l)
return c
Also it is better to iterate over list with for.
The results of the nested function calls are not used. You probably should replace the lines with c += nested_count(l,a) and c += nested_count(x,a), respectively.
You shouldn't mutate the list while iterate over it and you need to return the result from recursive calls. You could simplify function greatly by checking the type of l and if it's int then returning bool telling if it matches with a. In case l is a list just call nested_count recursively on its' items and sum the result:
def nested_count(l, a):
# Base case
if type(l) == int:
return l == a
return sum(nested_count(x, a) for x in l)
As others have mentioned you need to accumulate the return values from the recursive calls of nested_count to get the correct total.
Also, removing items from a list (or other collection) that you're iterating over can lead to unexpected results, see the SO Python Common Question Removing items from a list while iterating over the list for details and a few relevant SO pages, in particular: Removing from a list while iterating over it.
It's generally preferred to call isinstance rather than type to do type testing. The isinstance function can test for multiple types in one call, it will also return True if the object is a subclass of the specified type.
Here are a couple of one-liners that handle lists or tuples.
def nested_count(l, a):
return sum(nested_count(x, a) if isinstance(x, (list, tuple)) else x == a for x in l)
and
def nested_count(l, a):
return l.count(a) + sum(nested_count(x, a) for x in l if isinstance(x, (list, tuple)))
Why the heterogenous list? Why bother using typing if you're not going to type all the parameters? Why are you mutating the input?
from typing import List
def deep_count_elem (x:int, xs:List) -> int:
# done
if len(xs) == 0:
return 0
# if list, count in this list and recurse
elif isinstance(xs[0], list):
return deep_count_elem(x, xs[0]) + deep_count_elem(x, xs[1:])
# if element matches, add 1
elif x == xs[0]:
return 1 + deep_count_elem(x, xs[1:])
# otherwise add nothing, move to next element
else:
return deep_count_elem(x, xs[1:])
print(deep_count_elem(1, [[1,2,[4,[1],8],[1,3,2]],[1,1]])) # 5
This question already has answers here:
Remove an even/odd number from an odd/even Python list
(3 answers)
Closed 6 years ago.
So I understand placing even numbers in a new list but when there are a number of odd numbers in front I can only seem to call -1? and for an empty string I receive 'nothing' when it should also return -1
def first_even(items):
""" (list of int) -> int
Return the first even number from items. Return -1 if items contains no even numbers.
>>> first_even([1, 9, 3, 77, 13, 5, 8])
8
>>> first_even([7, 1])
-1
"""
even = []
for num in items:
if num % 2 == 0:
even.append(num)
else:
return -1
return even[0]
def first_even(items):
""" (list of int) -> int
Return the first even number from items. Return -1 if items contains no even numbers.
>>> first_even([1, 9, 3, 77, 13, 5, 8])
8
>>> first_even([7, 1])
-1
"""
for num in items:
if num % 2 == 0:
return num
return -1
The else block will be executed for the first odd number, this will terminate the for and discard all previous appends. Instead you want to return the even number as soon as it is found (you don't need the list) and then move the else to align with the for or outside the for:
def first_even(items):
for num in items:
if num % 2 == 0:
return num
else:
return -1
# or put the default value to return here
Reference for Python for/else block:
How can I make sense of the `else` clause of Python loops?
As others have noted, your else case is causing premature termination, and the use of a list is rather pointless. You could fix all this, see the other answers for minimalist fixes, but there is another option: Let Python built-ins do more of the work for you.
The next function will return the first item from an iterator, and with a second argument, if the iterator is empty, it returns the second argument instead. So give it a generator expression that lazily produces all the even numbers in the input, and it will pull the first one and return it. If none are found, the default case will return -1 for you:
def first_even(items):
# x & 1 == 0 is roughly equivalent and might be slightly faster
return next((x for x in items if x % 2 == 0), -1)
For the pathological sorts, this might be slightly faster, but it's far less intuitive to people who don't already know Python internals inside and out; I don't recommend it:
from itertools import filterfalse # ifilterfalse on Py2
def first_even(items):
# (1).__rand__ is roughly equivalent, and may be faster
return next(filterfalse((2).__rmod__, items), -1)
So I've recently picked up John Guttag's Introduction to Computation and Programming Using Python,the revised and expanded edition, after having worked through most of LPTHW. I am using the book in conjunction with MIT OCW 006. Now, I was trying to complete one of the Finger Exercises listed in the book, specifically the one of page 85, chapter 7, where the author asks you to implement a function using a try-except block:
def sumDigits(s):
"""Assumes s is a string
Returns the sum of the decimal digits in s
For example, if is is'a2b3c' it returns 5"""
This is my code:
def sumDigits(s):
try:
total = 0
list1 = [s]
new_list = [x for x in list1 if x.isdigit()]
for e in new_list:
total += new_list[e]
return total
except TypeError:
print "What you entered is not a string."
When I run this program in the IDLE using a test input, the total is always computed to be zero, indicating that none of the elements of new_list are being passed to the accumulator. Could someone suggest why that is? Thanks.
It seems like the errors have been pointed out already by Rafael but it is still important to note that the more pythonic way to approach this would be:
return sum([int(x) for x in s if x.isdigit()])
There are actually several errors with your code.
Let's break them down in detail
The main problem is located in these lines:
list1 = [s]
new_list = [x for x in list1 if x.isdigit()]
You should loop directly over the string first
new_list = [x for x in s if x.isdigit()] #s is the input string
When you create a new list as you did, the variable x in x for x in list1 will take place as elements of the list. So, in your case, the list will have only one element, which happen to be whole string (because you defined the list as [s]. As the whole string is not a digit, new_list will be an empty list.
That is why you are getting 0 as a return.
However, if you loop through the string directly, x will take place as each letter in the string, and then it will be possible to check whether x is digit or not.
It is also important to highlight that new_list[e] will raise IndexError. You should correct that for e only. The sintax of for e in new_list makes the local variable e assume each value inside the list, so you do not have to get the value via indexes: you can use e directly.
Finally, in order to sum the values in your new_list, the values should be integers (int) and not string (str), so you have to cast the values to int before summing (or, you can cast each element to int during the list comprehension, by using int(x) for x in s if x.isdigit() instead of x for x in s if x.isdigit()). Also, in order to check if the input is a string or not, you better use isinstance(s, basestring) if you're in python2, or isinstance(s, str) if you're using python3.
So the whole code would look like this :
def sumDigits(s):
if isinstance(s, basestring):
total = 0
new_list = [x for x in s if x.isdigit()]
for e in new_list:
total += int(e)
return total
else:
print "What you entered is not a string."
I'm working through the same book and the MITx: 6.00.1x course on edX; here's my solution:
def sumDigits(s):
'''
Assumes s is a string
Returns the sum of the decimal digits in s
For example, if s is 'a2b3c' it returns 5
'''
result = 0
try:
for i in range(len(s)):
if s[i].isdigit():
result += int(s[i])
return result
except:
print('Your input is not a string.')
Since we are to assume that s is a string, the except block should handle those cases where s is not a string. So simple, but it was not obvious to me at first.
You can use reduce method
reduce( (lambda x, y: x + y), [int(x) for x in new if x.isdigit()] )
I'm working through the same book too. I think we should use the try-except block on determining whether characters of string convertible to an integer. So here is my solution.
def sumDigits(s):
"""Assumes s is a string
Returns the sum of the decimal digits in s
For example, if s is 'a2b3c' it returns 5"""
sum = 0
for i in s:
try:
sum += int(i)
except ValueError:
None
return sum
I've been working on Bézier curve and I got everything working fine, but I want to make sure user will enter proper input.
I need statement checking if entered value is a list containing exactly two numbers, let it be integers or floats. More info is always welcome.
If anyone needs the code, here you go. (There's probably just a formula for it somewhere, and this here is inefficient. ^^)
#finding a point on vector based on start point, end and %
def findPoint(t, A, B):
'''
findPoint( 'float member of <0, 1>',
'coordinates of point A written as [x, y]',
'coordinates of point B written as [x, y]')
'''
x=(B[0]-A[0])*t+A[0]
y=(B[1]-A[1])*t+A[1]
return [x, y]
#find coordinates of a point on the bezier curve
def bezierCurve(t, *points):
pointList=[]
for X in points:
pointList.append(X)
while len(pointList) !=1:
tempList=[]
for i in xrange(0, len(pointList)-1):
tempList.append(findPoint(t, pointList[i], pointList[i+1]))
pointList=tempList
return pointList.pop()
You could check that all of the elements are int (or float) by using all
>>> l = [1,2,3]
>>> a = ['a','b','c']
>>> all(isinstance(i, int) for i in l)
True
>>> all(isinstance(i, int) for i in a)
False
Also you could check that len(list) == 2
So as a function it could be something like
def twoNumValues(myList):
return len(myList) == 2 and all(isinstance(i, int) or isinstance(i, float) for i in myList)
Or
def twoNumValues(myList):
return len(myList) == 2 and all(type(i) in [float, int] for i in myList)
This is a one line function to test the parameters you describe:
def check(l):
return len(l) == 2 and all(type(i) in (int, float) for i in l)
First check that the length is exactly 2, then check that both (all) of them are either int or float.