Exception handling without breaking, Python - python

This is probably a very basic question, but I looked through the python documentation on exceptions and couldn't find it.
I'm trying to read a bunch of specific values from a dictionary and insert slices of these into another dictionary.
for item in old_dicts:
try:
new_dict['key1'] = item['dog1'][0:5]
new_dict['key2'] = item['dog2'][0:10]
new_dict['key3'] = item['dog3'][0:3]
new_dict['key4'] = item['dog4'][3:11]
except KeyError:
pass
Now, if Python encounters a key error at ['dog1'], it seems to abort the current iteration and go to the next item in old_dicts. I'd like it to go to the next line in the loop instead. Do I have to insert an exception instruction for each row?

Make it a function:
def newdog(self, key, dog, a, b)
try:
new_dict[key] = item[dog][a:b]
except KeyError:
pass
I didn't run the above code but something like that should work, a modularization. Or what you could do is prepare so that it checks all values and removes all values that are not in the dictionary, but that will probably be more code than an exception for each row.

for item in old_dicts:
for i, (start, stop) in enumerate([(0,5), (0,10), (0,3), (3,11)], 1):
try:
new_dict['key' + str(i)] = item['dog' + str(i)][start:stop]
except KeyError:
pass

Assuming that you know the values in the keys will be valid, why not just forgo exceptions all together and check for the keys?
for item in old_dicts:
if 'dog1' in item:
new_dict['key1'] = item['dog1'][0:5]
if 'dog2' in item:
new_dict['key2'] = item['dog2'][0:10]
if 'dog3' in item:
new_dict['key3'] = item['dog3'][0:3]
if 'dog4' in item:
new_dict['key4'] = item['dog4'][3:11]

Yes, you do. It will be messy and unpleasant to look at, but you do need an exception handler for every call.
That said, you can write your code a little differently to make life easier on yourself. Consider this:
def set_new_dict_key(new_dict, key, item, path):
try:
for path_item in path:
item = item[path_item]
except KeyError:
pass
else:
new_dict[key] = item
for item in old_dicts:
set_new_dict_key(new_dict, 'key1', item, ['dog1', slice(0, 5)])
set_new_dict_key(new_dict, 'key2', item, ['dog2', slice(0, 10)])
set_new_dict_key(new_dict, 'key3', item, ['dog3', slice(0, 3)])
set_new_dict_key(new_dict, 'key4', item, ['dog4', slice(3, 11)])

Yes, you will. Best practice is to keep your try blocks as small as possible. Only write the code you possibly expect an exception from in the try block. Note that you also have an else for the try and except statements, that only gets executed when the try ran without an exception:
try:
// code that possibly throws exception
except Exception:
// handle exception
else:
// do stuff that should be done if there was no exception

Related

What is the proper way to write this exception handling?

I only want my list to have one element in it. There should be one exception raised if the list is empty and a different exception raised if the list has more than one element.
The following code accomplishes the task, but I don't think it's written as well as it should by any standards out there. Additionally, if the condition is met that the list is greater than one, I want it to print the python error "index out of range" as it does in the other condition. How can I improve upon this?
x = []
try:
if len(x) > 1:
raise Exception
else:
testVar = x[0]
except IndexError as e:
print(e)
print("list does not have any elements.")
except Exception as e:
print(e)
print("There are too many elements in the list.")
Here is a better way to write that.
def func(x):
if not x:
print("list does not have any elements.")
return None
if len(x) > 1:
print("There are too many elements in the list.")
return None
return x[0]
Note that if we omit the first three lines, then Python will automatically raise an IndexError when you refer to x[0].

Python : check if the nested dictionary exist

I have several nested dictionaries within lists, and I need to verify if a specific path exist e.g.
dict1['layer1']['layer2'][0]['layer3']
How can I check with an IF statement if the path is valid?
I was thinking to
if dict1['layer1']['layer2'][0]['layer3'] :
but it doesn't work
Here's the explicit short code with try/except:
try:
dict1['layer1']['layer2'][0]['layer3']
except KeyError:
present = False
else:
present = True
if present:
...
To get the element:
try:
obj = dict1['layer1']['layer2'][0]['layer3']
except KeyError:
obj = None # or whatever
I wanted to propose another solution, because I've been thinking about it too.
if not dict1.get("layer1", {}).get("layer2", {})[0].get("layer3", {}):
...
dict.get() attempts to get the key at each stage.
If the key is not present, an empty dict will be returned, instead of the nested dict (this is needed, because trying to call .get() on the default return of None will yield an AttributeError).
If the return is empty, it will evaluate to false.
So, this wouldn't work if the final result was an empty dict anyway, but if you can guarantee the results will be filled, this is an alternative that is fairly simple.
As far as I know, you've to go step by step, i.e.:
if 'layer1' in dict1:
if 'layer2' in dict1['layer1']
ans so on...
If you don't want to go the try/except route, you could whip up a quick method to do this:
def check_dict_path(d, *indices):
sentinel = object()
for index in indices:
d = d.get(index, sentinel)
if d is sentinel:
return False
return True
test_dict = {1: {'blah': {'blob': 4}}}
print check_dict_path(test_dict, 1, 'blah', 'blob') # True
print check_dict_path(test_dict, 1, 'blah', 'rob') # False
This might be redundant if you're also trying to retrieve the object at that location (rather than just verify whether the location exists). If that's the case, the above method can easily be updated accordingly.
Here is a similar question with the answer I would recommend:
Elegant way to check if a nested key exists in a python dict
Using recursive function:
def path_exists(path, dict_obj, index = 0):
if (type(dict_obj) is dict and path[index] in dict_obj.keys()):
if (len(path) > (index+1)):
return path_exists(path, dict_obj[path[index]], index + 1)
else:
return True
else:
return False
Where path is a list of strings representing the nested keys.

Python nested try except vs if, elif, else

I'm trying to get familiar with the best practices of Python. According to the Zen of Python it's easier to ask forgiveness than to ask for permission, however, it also says that flat is better than nested and readability counts. How would you deal with this:
I have 3 dictionaries. I have a key, and I want to test if the key is in a dictionary. The key will only be in one of them. Depending on which dictionary it's in, I want to do different stuff.
Using try/except, I come to the following solution:
try:
val = dict1[key]
except KeyError:
try:
val = dict2[key]
except KeyError:
try:
val = dict3[key]
except KeyError:
do_stuff_key_not_found()
else:
do_stuff_dict3()
else:
do_stuff_dict2()
else:
do_stuff_dict1()
According to Python's EAFP principle this would be the way to go, but it looks cluttered, and is not very readable.
A simpler solution would be:
if key in dict1:
val = dict1[key]
do_stuff_dict1()
elif key in dict2:
val = dict2[key]
do_stuff_dict2()
elif key in dict3:
val = dict3[key]
do_stuff_dict3()
else:
do_stuff_key_not_found()
What is the more Pythonic way of handling a case like this? Should I stick to the EAFP principle, or is flat and readability more important?
EAFP is a reasonable maxim in many situations, but it's not a dictum to be followed slavishly. In your example I would say there's nothing so horribly wrong with the if/elif version.
Both versions involve code repetition, and can thus become unwieldy if you have a large number of cases to handle. One way to deal with that is to pull out the dict/function pairs into a list, and then iterate over the list:
handlers = [ (dict1, do_stuff_dict1), (dict2, do_stuff_dict2), (dict3, do_stuff_dict3) ]
for whichDict, whichFunc in handlers:
try:
val = whichDict[key]
except KeyError:
continue
else:
whichFunc()
break
else:
do_stuff_not_found()
I prefer to use dict.get(key, default_value) to avoid exception handling, e.g.:
handlers = [(d1, func_1), (d2, func_2)]
handlers_found = [(d, func) for d, func in handlers if d.get(key)]
if handlers_found:
handlers_found[0][1]()
else:
do_stuff_not_found()
get(key[, default])
Return the value for key if key is in the
dictionary, else default. If default is not given, it defaults to
None, so that this method never raises a KeyError.
https://docs.python.org/2/library/stdtypes.html

Is there a way to get the next item in a list by knowing the current item's value?

my_list = ['apple', 'pear', 'orange', 'raspberry']
# I know that I'm always looking for pear.
print 'pear' in my_list # prints True
# I want to be able to get a key by its value.
pear_key = my_list['pear'].key # should be 1
# Print the next item in the list.
print my_list[pear_key + 1] # should print orange
I know that pear will always be an item in my list (not the position though), and I'm looking for a way to get the value of the next item in that list, either by getting the current key by knowing its value and advancing it by one (like I did in the example above) or by using something like my_list.next.
try:
pos = my_list.index('pear')
print my_list[pos + 1]
# orange
except IndexError as e:
pass # original value didn't exist or was end of list, so what's +1 mean?
You could of course pre-cache it, by using (think it's probably the itertools pair recipe)
from itertools import tee
fst, snd = tee(iter(my_list))
next(snd, None)
d = dict(zip(fst, snd))
But then you lose the fact of whether it was in the original list, or just didn't have a logical next value.
Use this:
>>> my_list[my_list.index('pear') + 1]
'orange'
Note that if this is the last value in the list, you get an exception IndexError.
While the simplest solution has been given, if you want to do this on a generic iterator, rather than a list, the simplest answer is to use itertools.dropwhile():
import itertools
def next_after(iterable, value):
i = itertools.dropwhile(lambda x: x != value, iterable)
next(i)
return next(i)
Which can be used like so:
>>> next_after(iter(my_list), "pear")
'orange'
Do note that this is a slower and less readable solution if you are working on a list. This is simply a note for an alternative situation.
You could also produce a version with more descriptive errors:
def next_after(iterable, value):
i = itertools.dropwhile(lambda x: x != value, iterable)
try:
next(i)
except StopIteration:
raise ValueError("{} is not in iterable".format(repr(value)))
try:
return next(i)
except StopIteration:
raise ValueError("{} is the last value in iterable".format(repr(value)))
You can use index over list to find a particular value: -
try:
print my_list[my_list.index('pear') + 1]
except (IndexError, ValueError), e:
print e

Idiomatic way of taking action on attempt to loop over an empty iterable

Suppose that I am looping over a iterable and would like to take some action if the iterator is empty. The two best ways that I can think of to do this are:
for i in iterable:
# do_something
if not iterable:
# do_something_else
and
empty = True
for i in iterable:
empty = False
# do_something
if empty:
# do_something_else
The first depends on the the iterable being a collection (so useless for when the iterable gets passed into the function/method where the loop is) and the second sets empty on every pass through the loop which seems ugly.
Is there another way that I'm missing or is the second alternative the best? It would be really cool if there was some clause that I could add to the loop statement that would handle this for me much like else makes not_found flags go away.
I am not looking for clever hacks.
I am not looking for solutions that involve a lot of code
I am looking for a simple language feature.
I am looking for a clear and pythonic way to iterate over an iterable and take some action if the iterable is empty that any experienced python programmer will be understand. If I could do it without setting a flag on every iteration, that would be fantastic.
If there is no simple idiom that does this, then forget about it.
I think this the the cleanest way to do this:
# first try with exceptions
def nonempty( iter ):
""" returns `iter` if iter is not empty, else raises TypeError """
try:
first = next(iter)
except StopIteration:
raise TypeError("Emtpy Iterator")
yield first
for item in iter:
yield item
# a version without exceptions. Seems nicer:
def isempty( iter ):
""" returns `(True, ())` if `iter` if is empty else `(False, iter)`
Don't use the original iterator! """
try:
first = next(iter)
except StopIteration:
return True, ()
else:
def iterator():
yield first
for item in iter:
yield item
return False, iterator()
for x in ([],[1]):
# first version
try:
list(nonempty(iter(x))) # trying to consume a empty iterator raises
except TypeError:
print x, "is empty"
else:
print x, "is not empty"
# with isempty
empty, it = isempty(iter(x))
print x, "is", ("empty" if empty else "not empty")
This is quite hackish, but you can delete i and then check if it exists after the loop (if not, the loop never happened):
try:
del i
except NameException: pass
for i in iterable:
do_something(i)
try:
del i
except NameException:
do_something_else()
I think that's probably uglier than just using a flag though
Update 2
I liked Odomontois' answer. IMHO it is better suited to this problem than what I have written below.
Update
(After reading the OP's comment and edited question) You can do that too. See below:
def with_divisible(n, a, b, f):
it = (i for i in xrange(a, b) if not i % n)
for i in wrapper(it):
f(i)
>>> with_divisible(1, 1, 1, lambda x: x)
Traceback (most recent call last):
File "<pyshell#55>", line 1, in <module>
with_divisible(1, 1, 1, lambda x: x)
File "<pyshell#54>", line 3, in with_divisible
for i in wrapper(it):
File "<pyshell#46>", line 4, in wrapper
raise EmptyIterableException("Empty")
EmptyIterableException: Empty
>>> with_divisible(7, 1, 21, lambda x: x)
7
14
...Snipped...
raise EmptyIterableException("Empty")
EmptyIterableException: Empty
Original Answer
Interesting problem. I did some experiments and came up with the following:
class EmptyIterableException(Exception):
pass
def wrapper(iterable):
for each in iterable:
yield each
raise EmptyIterableException("Empty")
try:
for each in wrapper(iterable):
do_something(each)
except EmptyIterableException, e:
do_something_else()
if not map(do_something_callable,iterable) :
# do something else
The general way forward if an iterator is to be partially checked before being consumed is to use itertools.tee. This way we can have two copies of the iterator and check one for emptiness while still consuming the other copy from the start.
from itertools import tee
it1, it2 = tee(iterable)
try:
it1.next()
for i in it2:
do_some_action(i) #iterator is not empty
except StopIteration:
do_empty_action() #iterator is empty
The StopIteration exception is bound to be a result of the call to it1.next(), as any StopIteration exceptions raised froom inside the loop will terminate that loop.
Edit: for those who don't like such exceptions, islice can be used to set up a single step loop:
from itertools import tee, islice
it1, it2 = tee(iterable)
for _ in islice(it1, 1):
#loop entered if iterator is not empty
for i in it2:
do_some_action(i)
break #if loop entered don't execute the else section
else:
do_empty_action()
I personally prefer the first style. YMMV.
What about reversing "if" and "for":
if iterable:
for i in iterable:
do_something(i)
else:
do_something_else()
OK, this does not work!
Here is an other solution: http://code.activestate.com/recipes/413614-testing-for-an-empty-iterator/
This is a combination of Michael Mrozek's and FM's answers:
def with_divisible(n, a, b, f):
'''apply f to every integer x such that n divides x and a <= x < b'''
it = (i for i in xrange(a, b) if not i % n)
for i in it:
f(i)
try: i # test if `it` was empty
except NameError: print('do something else')
def g(i):
print i,
with_divisible( 3, 1, 10, g) # Prints 3 6 9.
with_divisible(33, 1, 10, g) # Prints "do something else"
Generators have a 'gi_frame' property which is None once the generator is exhausted, but only after StopIteration has been raised. If that's acceptable, here's something you could try:
import types
def do(x, f, f_empty):
if type(x) == types.GeneratorType:
# generators have a 'gi_frame' property,
# which is None once the generator is exhausted
if x.gi_frame:
# not empty
return f(x)
return f_empty(x)
if x:
return f(x)
return f_empty(x)
def nempty(lst):
print lst, 'not empty'
def empty(lst):
print 'Twas empty!'
# lists
do([2,3,4], nempty, empty)
do([], nempty, empty)
# generators
do((i for i in range(5)), nempty, empty)
gen = (i for i in range(1))
gen.next()
try:
gen.next()
except StopIteration:
pass
do(gen, nempty, empty)

Categories

Resources