How to say in Pythonese - do something unless it causes an error (without resorting to multilevel try/execpt blocks)

Viewed 79

This is a little difficult to explain, so let's hope I'm expressing the problem coherently:

Say I have this list:

my_list = ["a string", 45, 0.5]

The critical point to understand in order to see where the question comes from is that my_list is generated by another function; I don't know ahead of time anything about my_list, specifically its length and the datatype of any of its members.

Next, say that every time <my_list> is generated, there is a number of predetermined operations I want to perform on it. For example, I want to:

my_text = my_list[1]+"hello"
some_var = my_list[10]
mini_list = my_list[0].split('s')[1]
my_sum = my_list[7]+2

etc. The important point here is that it's a large number of operations.

Obviously, some of these operations would succeed with any given my_list and some would fail and, importantly, those which fail will do so with an unpredictable Error type; but I need to run all of them on every generation of my_list.

One obvious solution would be to use try/except on each of these operations:

try:
  my_text = my_list[1]+"hello"
except:    
  my_text = "None"

try:
  some_var = my_list[10]
except:
  some_var = "couldn't do it"

etc.

But with a large number of operations, this gets very cumbersome. I looked into the various questions about multiple try/excepts, but unless I'm missing something, they don't address this.

Based on someone's suggestion (sorry, lost the link), I tried to create a function with a built-in try/except, create another list of these operations, and send each operation to the function. Something along the lines of

def careful(op):
  try:
    return op
  else:
    return "None"

And use it with, for example, the first operation:

my_text = careful(my_list[1]+"hello")

The problem is python seems to evaluate the careful() argument before it's sent out to the function and the error is generated before it can be caught...

So I guess I'm looking for a form of a ternary operator that can do something like:

my text = my_list[1]+"hello" if (this doesn't cause any type of error) else "None"

But, if one exist, I couldn't find it...

Any ideas would be welcome and sorry for the long post.

3 Answers

Maybe something like this?

def careful(op, default):
  ret = default
  try:
    ret = computation()
  else:
    pass
  return ret

If you must do this, consider keeping a collection of the operations as strings and calling exec on them in a loop

actions = [
    'my_text   = my_list[1]+"hello"',
    'some_var  = my_list[10]',
    'mini_list = my_list[0].split("s")[1]',
    'my_sum    = my_list[7]+2',
]

If you make this collection a dict, you may also assign a default

Note that if an action default (or part of an action string) is meant to be a string, it must be quoted twice. Consider using block-quotes for this if you already have complex escaping, like returning a raw strings or a string representing a regular expression

{
    "foo = bar": r"""r'[\w]+baz.*'"""
}

complete example:

>>> actions_defaults = {
...     'my_text   = my_list[1]+"hello"':       '"None"',
...     'some_var  = my_list[10]':              '"couldn\'t do it"',
...     'mini_list = my_list[0].split("s")[1]': '"None"',
...     'my_sum    = my_list[7]+2':             '"None"',
... }
>>>
>>> for action, default in actions_defaults.items():
...     try:
...         exec(action)
...     except Exception:  # consider logging error
...         exec("{} = {}".format(action.split("=")[0], default))
...
>>> my_text
'None'
>>> some_var
"couldn't do it"

Other notes

  • this is pretty evil
  • declaring your vars before running to be their default values is probably better/clearer (sufficient to pass in the except block, as the assignment will fail)
  • you may run into weird scoping and need to access some vars via locals()

This sounds like an XY Problem

If you can make changes to the source logic, returning a dict may be a much better solution. Then you can determine if a key exists before doing some action, and potentially also look up the action which should be taken if the key exists in another dict.

Related