How to pass dictionary key as function argument in Python - python

I'm a python and programming beginner and I've been trying to create a function to check whether a given key exists inside a dictionary or not and retrun boolean. This topic was helpful but did not solve my function parameter problem.
I found many topics related to passing a dictionary as argument in a function but none stating how to do so with a key, couldn't find an answer to my specific problem in here.
When I use my code inside the main program, it works fine:
if "myKey" in myDict:
answ = True
print(myKey, " is there!")
else:
answ = False
print(myKey, " is not there.")
However, trying to make a function of it and then calling it doesn't work, it doesn't return an error either, just nothing happens nor gets printed.
def checkIfThere(myKey, myDict):
#for i in myDict:
if myKey in myDict:
return True
print(myKey, "is there!")
else:
return False
print(myKey, "is not there.")
That I've tried calling with the following:
checkIfThere("thisIsAKey", myDict)
checkIfThere(thisIsAKey, myDict)
checkIfThere("\"thisIsAKey\"", myDict)
What am I missing?
Is it just not feasible to pass a dictionary key as argument to a function?

The problem is that your function will stop execution, and return control to the caller, when it encounters a return statement. Note, you are also discarding the return value (since you don't assign the result of the call to a variable).
Consider:
>>> def some_func(x):
... return
... print(x)
...
>>> y = some_func(42)
Notice, the print function never ran.
Generally, you should let the function do the work, and let the caller do the printing. So, your function could be written (in a more streamlined manner):
>>> def check_if_there(key, adict):
... return key in adict
...
>>> is_in = check_if_there('a', {'b':2})
>>> print(is_in)
False
Notice, this function's responsibility is simply to check if a key in a dict. As you learn to program, you will find it useful to split functions into re-usable, composable parts. So, another function could have the reponsibility of printing:
>>> def tell_if_there(key, adict):
... if check_if_there(key, adict):
... print(key, " is there!")
... else:
... print(key, " is not there.")
...
>>> tell_if_there('a', {'b':2})
a is not there.
>>> tell_if_there('b', {'b':2})
b is there!

Your functions works fine!
But the print statement should be outside of the function. Try this.
1) Define your function without print statement
def checkIfThere(myKey, myDict):
for i in myDict:
if myKey in myDict:
return True
else:
return False
This will return True or False depending on myKey is one of the keys of myDict.
2) Then, run the followings.
if checkIfThere(myKey, myDict):
print(myKey, " is there!")
else:
print(myKey, " is not there.")
This will print myKey is there if the function above returns True; otherwise myKey is not there.
Thank you.

The problem with your function is that you are returning from the function before printing anything.
You could remove the return statement from your function in order to make it work.

Related

Can return only be used once in a block of code?

I'm in the process of learning python, and I can't wrap my head around a piece of code in the book:
def find_value(List, value):
for i in range(len(List)):
if List[i] == value:
return i
return -1
I've tried running the code, and it returns the index if the value is in it, and returns -1 if it isn't, but what I don't understand is since the 'return -1' is outside the for loop, and if statement, should it not be run every time the code is executed?
Or does return only get processed once, the first time it is called?
I just want to make sure I understand this concept before moving on.
Thanks in advance
No, you can have as many return statements in as many places as you like:
def function():
... # do something
return 1
return
return [3, 4, 5]
return next(__import__('os').walk('.'))[-1:-1000000000:-1]
Though the important thing to keep in mind that only the first return that your function encounters will count, and anything else that would've succeeded it is not touched.
In the function above, you'll get 1 as the result and nothing else.
This sort of "flow of control" is why you can even do weird stuff like this -
def foo():
return True
whatIsThisIdontEven__somethingWeird.jpg # would ordinarily throw RuntimeErrors anywhere else
print(foo())
# True
In your case, it entirely depends on your code flow at runtime, but you'll still end up encountering only one return, and consequently returning only once.
Note that one difference is in the case of try-except-finally, where the return in the final clause always wins.
def function():
try:
... # do something
return 1
except SomeException:
... # do something else
finally:
return 2
In the case of normal execution, you'll encounter return 1 in try, but because of the semantics of the finally clause, you'd still end up returning 2. I believe this is the only exception.
Now, yield, on the other hand, is a different matter...
Once the function sees a return statement, the function will end and return the variable in the return statement. So the rest of the function will not be executed once the function comes across a return statement.
The return statement causes your function to exit and hand back a
value to its caller. The point of functions in general is to take in
inputs and return something.
Keep in mind : function return one at a time by memory .
So when you start the loop and then 'if' condition goes true so function return and exit.
if List[i] == value:
return i
and if you have to return many items then don't return the output instead of store the output in a list and return that list at last .
def find_value(List, value):
return_list=[]
for i in range(len(List)):
if List[i] == value:
return_list.append(i)
return return_list
In you code you wanted two return so you can try conditional return like this:
def find_value(List, value):
return_list=[]
for i in range(len(List)):
if List[i] == value:
return_list.append(i)
if return_list:
return return_list
else:
return -1
print(find_value([1,2,3,4,5,6],4))

Printing vs Returning function for a dictionary

I have the following two codes, similar except one function prints, while another returns the keys in a dictionary.
My question is, why does the one that returns, only show the first line when the print is called on the function?
**VERSION 1**
ColourOfCats = {"timmy":"black","sam":"white","john":"green"}
def catsfunc (dict):
for i in dict.keys():
return (i)
X = catsfunc(ColourOfCats)
print (X)
**VERSION 2**
ColourOfCats = {"timmy":"black","sam":"white","john":"green"}
def catsfunc (dict):
for i in dict.keys():
print (i)
catsfunc(ColourOfCats)
Thank you for your assistance.
Because a function can only return once.
The return statement terminates the execution of a function and
returns control to the calling function. Execution resumes in the
calling function at the point immediately following the call.
So in your VERSION 1 at line 8 when it return the i then return statement terminates the execution of a function and it so for loop doesn't go for second iteration for next value of dict.keys().
If you want to return all the result then never return iterator , instead store the output in a list , dict , set etc and at last return that
Here is an example for you:
ColourOfCats = {"timmy":"black","sam":"white","john":"green"}
def catsfunc (dict):
result=[]
for i in dict.keys():
result.append(i) #instead of return here , store the output to a list
return result #now return
X = catsfunc(ColourOfCats)
print (X)
output:
['timmy', 'john', 'sam']
When you use return statement in function function will return the value and stops the execution and return control flow to calling line; hence even if you use for loop; when function returns first value another loops aren't executing whereas when you use print it is just printing the statement which is not causing to break the function flow.
However you can make generator function using yield keyword as below:
ColourOfCats = {"timmy":"black","sam":"white","john":"green"}
def catsfunc (dict):
for i in dict.keys():
yield i
X = catsfunc(ColourOfCats) # X is now generator object
for items in X: # iterate generator object
print (items)

How to check if a dictionary is empty?

I am trying to check if a dictionary is empty but it doesn't behave properly. It just skips it and displays ONLINE without anything aside from the display the message. Any ideas why ?
def isEmpty(self, dictionary):
for element in dictionary:
if element:
return True
return False
def onMessage(self, socket, message):
if self.isEmpty(self.users) == False:
socket.send("Nobody is online, please use REGISTER command" \
" in order to register into the server")
else:
socket.send("ONLINE " + ' ' .join(self.users.keys()))
Empty dictionaries evaluate to False in Python:
>>> dct = {}
>>> bool(dct)
False
>>> not dct
True
>>>
Thus, your isEmpty function is unnecessary. All you need to do is:
def onMessage(self, socket, message):
if not self.users:
socket.send("Nobody is online, please use REGISTER command" \
" in order to register into the server")
else:
socket.send("ONLINE " + ' ' .join(self.users.keys()))
Here are three ways you can check if dict is empty. I prefer using the first way only though. The other two ways are way too wordy.
test_dict = {}
if not test_dict:
print "Dict is Empty"
if not bool(test_dict):
print "Dict is Empty"
if len(test_dict) == 0:
print "Dict is Empty"
d = {}
print(len(d.keys()))
If the length is zero, it means that the dict is empty.
Simple ways to check an empty dict are below:
a = {}
if a == {}:
print ('empty dict')
if not a:
print ('empty dict')
Method 1 is more strict, because when a = None, method 1 will provide the correct result, but method 2 will give an incorrect result.
A dictionary can be automatically cast to boolean which evaluates to False for empty dictionary and True for non-empty dictionary.
if myDictionary: non_empty_clause()
else: empty_clause()
If this looks too idiomatic, you can also test len(myDictionary) for zero, or set(myDictionary.keys()) for an empty set, or simply test for equality with {}.
The isEmpty function is not only unnecessary but also your implementation has multiple issues that I can spot prima-facie.
The return False statement is indented one level too deep. It should be outside the for loop and at the same level as the for statement. As a result, your code will process only one, arbitrarily selected key, if a key exists. If a key does not exist, the function will return None, which will be cast to boolean False. Ouch! All the empty dictionaries will be classified as false-nagatives.
If the dictionary is not empty, then the code will process only one key and return its value cast to boolean. You cannot even assume that the same key is evaluated each time you call it. So there will be false positives.
Let us say you correct the indentation of the return False statement and bring it outside the for loop. Then what you get is the boolean OR of all the keys, or False if the dictionary empty. Still you will have false positives and false negatives. Do the correction and test against the following dictionary for an evidence.
myDictionary={0:'zero', '':'Empty string', None:'None value', False:'Boolean False value', ():'Empty tuple'}
1st Way
len(given_dic_obj)
It returns 0 if there are no elements.
Else, returns the size of the dictionary.
2nd Way
bool(given_dic_object)
Returns False if the dictionary is empty, else return True.
You can also use get(). Initially I believed it to only check if key existed.
>>> d = { 'a':1, 'b':2, 'c':{}}
>>> bool(d.get('c'))
False
>>> d['c']['e']=1
>>> bool(d.get('c'))
True
What I like with get is that it does not trigger an exception, so it makes it easy to traverse large structures.
use 'any'
dict = {}
if any(dict) :
# true
# dictionary is not empty
else :
# false
# dictionary is empty

python - Simulating 'else' in dictionary switch statements

I'm working on a project which used a load of If, Elif, Elif, ...Else structures, which I later changed for switch-like statements, as shown here and here.
How would I go about adding a general "Hey, that option doesn't exist" case similar to an Else in an If, Elif, Else statement - something that gets executed if none of the Ifs or Elifs get to run?
If the else is really not an exceptional situation, would it not be better to use the optional parameter for get?
>>> choices = {1:'one', 2:'two'}
>>> print choices.get(n, 'too big!')
>>> n = 1
>>> print choices.get(n, 'too big!')
one
>>> n = 5
>>> print choices.get(n, 'too big!')
too big!
You could catch the KeyError error that ensues when a value is not found in the map, and return or process there a default value. For example, with n = 3 this piece of code:
if n == 1:
print 'one'
elif n == 2:
print 'two'
else:
print 'too big!'
Becomes this:
choices = {1:'one', 2:'two'}
try:
print choices[n]
except KeyError:
print 'too big!'
Either way, 'too big!' gets printed on the console.
The first article you linked to had a very clean solution:
response_map = {
"this": do_this_with,
"that": do_that_with,
"huh": duh
}
response_map.get( response, prevent_horrible_crash )( data )
This will call prevent_horrible_crash if response is not one of the three choices listed in response_map.
Let's say you have a function f(a,b) and different setups of parameters according to the value of some variable x. So you want to execute f with a=1 and b=3 if x='Monday' and if x='Saturday' you want to execute f with a=5 and b=9. Otherwise you will print that such value of x is not supported.
I would do
from functools import partial
def f(a,b):
print("A is %s and B is %s" % (a,b))
def main(x):
switcher = {
"Monday": partial(f,a=1, b=3),
"Saturday": partial(f, a=5, b=9)
}
if x not in switcher.keys():
print("X value not supported")
return
switcher[x]()
this way f is not executed on declaration of switcher but at the last line.
Some one-line alternatives:
choices = {1:'one', 2:'two'}
key = 3
# returns the provided default value if the key is not in the dictionary
print(choices[key] if key in choices else 'default_value')
# or using the dictionary get() method
print(choices.get(key, 'default_value')

Using for...else in Python generators

I'm a big fan of Python's for...else syntax - it's surprising how often it's applicable, and how effectively it can simplify code.
However, I've not figured out a nice way to use it in a generator, for example:
def iterate(i):
for value in i:
yield value
else:
print 'i is empty'
In the above example, I'd like the print statement to be executed only if i is empty. However, as else only respects break and return, it is always executed, regardless of the length of i.
If it's impossible to use for...else in this way, what's the best approach to this so that the print statement is only executed when nothing is yielded?
You're breaking the definition of a generator, which should throw a StopIteration exception when iteration is complete (which is automatically handled by a return statement in a generator function)
So:
def iterate(i):
for value in i:
yield value
return
Best to let the calling code handle the case of an empty iterator:
count = 0
for value in iterate(range([])):
print value
count += 1
else:
if count == 0:
print "list was empty"
Might be a cleaner way of doing the above, but that ought to work fine, and doesn't fall into any of the common 'treating an iterator like a list' traps below.
There are a couple ways of doing this. You could always use the Iterator directly:
def iterate(i):
try:
i_iter = iter(i)
next = i_iter.next()
except StopIteration:
print 'i is empty'
return
while True:
yield next
next = i_iter.next()
But if you know more about what to expect from the argument i, you can be more concise:
def iterate(i):
if i: # or if len(i) == 0
for next in i:
yield next
else:
print 'i is empty'
raise StopIteration()
Summing up some of the earlier answers, it could be solved like this:
def iterate(i):
empty = True
for value in i:
yield value
empty = False
if empty:
print "empty"
so there really is no "else" clause involved.
As you note, for..else only detects a break. So it's only applicable when you look for something and then stop.
It's not applicable to your purpose not because it's a generator, but because you want to process all elements, without stopping (because you want to yield them all, but that's not the point).
So generator or not, you really need a boolean, as in Ber's solution.
If it's impossible to use for...else in this way, what's the best approach to this so that the print statement is only executed when nothing is yielded?
Maximum i can think of:
>>> empty = True
>>> for i in [1,2]:
... empty = False
... if empty:
... print 'empty'
...
>>>
>>>
>>> empty = True
>>> for i in []:
... empty = False
... if empty:
... print 'empty'
...
empty
>>>
What about simple if-else?
def iterate(i):
if len(i) == 0: print 'i is empty'
else:
for value in i:
yield value

Categories