I'm new to programming so this is probably a silly question!
I'm trying to write a function that takes a list of letter grades and returns a Boolean true or false depending on whether it is a passing grade
## Calculate total units passed
def unitPassed(letterGrade):
if letterGrade == 'N':
passed = False
else:
if letterGrade == 'NCN':
passed = False
else:
if letterGrade == 'WN':
passed = False
else:
True
return passed
unitsPassed = map(unitPassed,courseGradeLetter)
I've attempted to do this by creating a function that tests if a grade is a passing grade, and then map this over a list of grade.
The problem is that I get the following error for a list that contains more than one element:
local variable 'passed' referenced before assignment
If I try it with a list containing one element I get the correct result.
Any ideas why this is?
Thanks.
--
Thanks everyone, your answers have helped me a lot!
Some have already pointed your error and posted the most pythonic solutions, but anyway - here's another code review, but this time a step by step one:
First point: learn to use elif instead of nesting if clauses in else clauses:
def unitPassed(letterGrade):
if letterGrade == 'N':
passed = False
elif letterGrade == 'NCN':
passed = False
elif letterGrade == 'WN':
passed = False
else:
passed = True
return passed
As you can see this is already more readable.
Second point: in Python we favor "early returns" - ie, in this kind of tests, instead of setting a variable in all branches and returning it at the end, we return directly (which avoids elif/else chains etc):
def unitPassed(letterGrade):
if letterGrade == 'N':
return False
if letterGrade == 'NCN':
return False
if letterGrade == 'WN':
return False
return True
Which makes the code even more straightforward.
And of course the last refactoring is to avoid the multiple tests when possible, as posted by Dadep and digitake (but using a tuple instead of a list because we dont need the overhead of a list here):
def unitPassed(letterGrade):
return letterGrade not in ("N", "NCN", "WN")
Also, you are using map() in your code snippet but while map() is still perfectly valid and appropriate, the more idiomatic (and marginally faster) version here would be a list comprehension:
passed = [unitPassed(letter) for letter in courseGradeLetter]
And since unitPassed is now basically a simple short expression, you can just inline it (unless of course you need this function elsewhere):
passed = [letter not in ('N', 'NCN', 'NWN') for letter in courseGradeLetter]
how about this
def unitPassed(letterGrade):
return letterGrade not in ['N', 'NCN', 'WN']
unitsPassed = map(unitPassed,courseGradeLetter)
you can redefine your function as :
>>> def unitPassed(letterGrade):
... GP=['N', 'NCN', 'WN']
... if letterGrade in GP:
... return False
... return True
...
>>> letterGrade='DE'
>>> unitPassed(letterGrade)
True
>>> letterGrade='NCN'
>>> unitPassed(letterGrade)
False
The error is in the 3rd else statement, i.e. in line 13.
Change True to passed = True
Also, you can make your function clean,
def unitPassed(letterGrade):
if letterGrade == 'N':
passed = False
elif letterGrade == 'NCN':
passed = False
elif letterGrade == 'WN':
passed = False
else:
passed = True
return passed
Related
why does the output of these two functions give different outputs when the logic or idea is the same and they are working with the same string?
def solution(inputString):
a = ""
b = a[::-1]
if a == b:
return True
else:
return False
print(solution("az"))
def ans(something):
if something == reversed(something):
print(True)
else:
print(False)
ans('az')
This is I think because you are not using your inputString parameter in the function solution(). This may be closer to what you want:
def solution(inputString):
a = inputString
b = a[::-1]
if a == b:
return True
else:
return False
when the logic or idea is the same
No, the solution and ans functions have different logic.
solution uses the common way of reversing a string, thats fine
However, the second function uses reversed() function, which does:
reversed(seq)
Return a reverse iterator. seq must be an object which has a __reversed__() method or supports the sequence protocol ...
It does not return the reversed string as you'd probably expected.
To put that in perspective, the following code returns False:
print("oof" == reversed("foo"))
Because the return value of reversed("foo") is an <reversed object> and not the reversed String.
I wanted to make a function that allows someone to guess the value of any key in the dictionary, and find out if they were right or wrong. If the key exists in the dictionary and that value is the correct value, then the function should return true. In all other cases, it should return false. But it prints out False for every key in the dictionary not just once. Thanks.
WaywardSonDict = {"Artist":"Kansas","Song":"Carry on Wayward Son","Genre":"Hard Rock","Album":"Leftoverture","Writer":"Kerry Livgren"}
def valueDict(key,value):
for i, j in WaywardSonDict.items():
if i == key and j == value:
print(True)
else:
print(False)
valueDict("Genre","Hard Rock")
You're printing True/False at every iteration of your loop. Instead, you could return True when there's a match and False at the end of the loop (i.e., when there is no match):
def valueDict(key, value):
for k in WaywardSonDict:
if k == key and WaywardSonDict[k] == value:
return True
return False
Alternatively, instead of looping over the entire dictionary, you could use .get which returns None if the key doesn't exist in the dictionary:
def valueDict(key, value):
if WaywardSonDict.get(key)==value:
return True
return False
Here you go:
WaywardSonDict = {"Artist":"Kansas","Song":"Carry on Wayward Son","Genre":"Hard Rock","Album":"Leftoverture","Writer":"Kerry Livgren"}
def valueDict(key,value):
if (key,value) in WaywardSonDict.items():
print('True')
else:
print('False')
valueDict("Genre","Hard Rock")
OUTPUT
True
def is_in_wayword_son_dict(key, value):
if WaywordSonDict.get(key, '') == value:
return True
return False
Get the value of key from the dictionary if it exists, compare to passed value, return true if it matches, false otherwise.
You are currently using a for each loop to go through every value in the list to check if it has both the key and value that you are looking for. Part of the appeal of using a dictionary is that you don't have to iterate through it to find the key/value pair that you want, you can get the value of the entry you want through indexing it with WaywardSonDict["Genre"].
In your instance, the code could read:
if key in WaywardSonDict && WaywardSonDict[key] == value:
return true;
return false;
An easy way to do this is to have a boolean variable that is set default to False, and changes to True if you find a match. Then, you just print out the variable at the end:
WaywardSonDict = {"Artist":"Kansas","Song":"Carry on Wayward Son","Genre":"Hard Rock","Album":"Leftoverture","Writer":"Kerry Livgren"}
def valueDict(key,value):
guessed = False
for i, j in WaywardSonDict.items():
if i == key and j == value:
guessed = True
break
print(guessed)
To expand on not_speshal's answer:
What lead to multiple boolean prints?
The issue's in your code are:
printing in the loop will print for each entry in dict
not returning, means looping through the complete dictionary
Solving iteratively
You can solve these issues, improve code and learn step by step (iteratively).
1. make it return
# rename from `valueDict` to a question in order to signal boolean return
def hasKeyWithValue(key,value):
for i, j in WaywardSonDict.items():
if i == key and j == value:
return True
# no else, simply continue looping to the end
return False # if not found and returned, finally return False
print(hasKeyWithValue('Genre', 'Hard Rock') # prints: True
Design advice: rename the function to signal expected boolean. This can be done by naming it like a question.
2. use dict's getter function instead of loop
def hasKeyWithValue(key,value):
if WaywardSonDict.get(key) == value:
return True
else:
return False
Then you need a complete if-else.
Alternatively you could substitute the else by a default return:
def hasKeyWithValue(key,value):
if WaywardSonDict.get(key) == value:
return True # that's the "unexpected"
return False # that's the default
Note: .get is special here, because it will return None if the key is not found.
This allows for non-existence checks like hasKeyWithValue('Year',None) which will return True because the given dictionary has no entry with key Year.
3. simplify the if-return
def hasKeyWithValue(key,value):
return (WaywardSonDict.get(key) == value)
Simplify the test. The if-else statement is obsolete, because the boolean expression (inside parentheses) evaluates to either True or False.
4. inline the simple test
key = 'Genre'
value = 'Hard Rock'
print(WaywardSonDict.get(key) == value)
So the hasKeyWithValue function has been replaced by a short ideomatic dictionary-test (that most Python developers can understand).
5. improve readability
# format like a table to ease reading
WaywardSonDict = {
"Artist" : "Kansas",
"Song" : "Carry on Wayward Son",
"Genre" : "Hard Rock",
"Album" : "Leftoverture",
"Writer" : "Kerry Livgren"
}
This way you can easily scan the dictionary entries like a check-list.
I am trying to write a program about poker game and for the comparison part I called (returned) multiples values for each set up.
In this case, I returned an array, one of them was a boolien and one of them was an integer. But when I tried to use them in another function I get an error ('bool' object is not subscriptable) and I dont know why. My whole code is nearly 150 line and for running it you ll need extra file so I ll share some parts of it.
these are the set up parts for each combination
def straight(n):
F = converter(n)
if consecutive(F) == True:
return [True, F[0]]
return False
def full_house(n):
F = converter(n)
if len(set(F)) == 2:
for i in F:
if F.count(i) == 3:
return [True, i]
return False
this is the part where I will rank them
def ranking(n, k):
if n == "AKQJT" and flush(k) == True:
return 9
elif straight(n)[0]== True and flush(k) == True:
return [8,straight(n)[1]]
elif four_kind(n)[0]== True:
return [7,four_kind(n)[1]]
elif (full_house(n))[0]== True:
return [6,full_house(n)[1]]
elif flush(k) == True:
return 5
elif (straight(n))[0]== True:
return [4,straight(n)[1]]
for example when I try
print(ranking("44447","DDDDD"))
I get an error
elif straight(n)[0]== True and flush(k) == True: line ...
TypeError: 'bool' object is not subscriptable
But interstingly when I try the straight flush (the second elif part tests it). For example,
print(ranking("23456","DDDDD")
I get an answer like
[8,6]
which its the true answer but then again I get the same error.
In the default case, you do not return an array:
return False
Change it to something like
return [False, None]
or whatever makes sense for your situation.
Please checkout what you are returning through straight(n). I believe in this case you are trying to return False. So, Boolean is not subscript-able.
If you get get straight(n) as False. You cannot write if-elif conditions to verify their cases. You need to design nested loops for cases straight(n) is False and straight(n) is not equal to False.
I am new to Python 3 and currently practicing with functions. I wanted to first write a function that returns True or False depending on whether its parameter (int) is even.
def isEven(number):
if number % 2 == 0:
return True
elif number % 2 != 0:
return False
This worked as I could print the return value with the print() function.
However, I had problems when I wanted to write a second function, which is the isOdd() function. I wanted it to negate the whatever return value of isEven(). I tried:
def isOdd (number):
return not isEven(number)
Is there a more efficient way, say, by creating a conditional statement in isOdd()?
Then you might not need isOdd(), since isEven() will return false then.
You can also eliminate the if-elif statement:
def isEven(number):
return number%2 == 0
If we talk about only this example then you can use if. But if isEven is bigger in realtime world, you should call it in isOdd
I'd minimize isEven with like this
def isEven(number):
return number % 2 == 0
def func_palindrome(stri):
if len(stri) == 0 or 1:
return True
if stri[0] != stri[-1]:
return False
return func_palindrome(stri[1:-1])
I'm not sure if this function, that checks if the string is a palindrome can be considered as a recursive code or not. That becuase I can simply change the last return value from return func_palindrome(str[1:-1]) to True and nothing will change
Your issue is here:
if len(str) == 0 or 1 :
should be
if len(str) == 0 or len(str) == 1:
By just doing if len(str) == 0 or 1 it is evaluating to True always as it is interpreted as (len(str) == 0) or 1
Also, I would rename str to something else, as str is a builtin type
Any function that calls itself is technically a recursive function.
Any function that checks its arguments for a "base case", and otherwise calls itself with a "smaller" version of its arguments, is a usefully recursive function.
Of course a recursive function can still be broken, or even pointless. For example, consider this function:
def recursive_length(a):
if not a:
return 0
return 0 + recursive_length(a[1:])
My stupid bug in the last line doesn't mean this is not a recursive function. I'm recursively summing up N copies of the number 0, instead of N copies of the number 1, so I could have done the same thing by just writing return 0. But that's just because the sum of N copies of 0 is always 0, not because my function fails to be recursive.
So, what if there were a problem in the base case?
def recursive_length(a):
if a is not None:
return 0
return 1 + recursive_length(a[1:])
Now, it never actually recurses… but it's still a recursive function. It's just a recursive function with a bug in the base case.
You don't need to do this recursively, but you can, particularly if you don't mind a lot of extra space and poor performance for long strings.
Here are two nonrecursive ways:
#!/usr/bin/python3
def is_palindrome1(string1):
string2_list = list(string1)
string2_list.reverse()
string2 = ''.join(string2_list)
return string1 == string2
def is_palindrome2(string):
len_string = len(string)
for index in range(len_string // 2):
character1 = string[index:index+1]
character2 = string[len_string-index-1:len_string-index]
if character1 == character2:
# This character is good
pass
else:
return False
# all characters matched
return True
for string in [ '393', '339', 'aibohphobia', 'aibobphobia' ]:
assert is_palindrome1(string) == is_palindrome2(string)
print(is_palindrome1(string))
print(is_palindrome2(string))