General decorator to wrap try except in python?

后端 未结 8 1716
庸人自扰
庸人自扰 2020-12-12 16:25

I\'d interacting with a lot of deeply nested json I didn\'t write, and would like to make my python script more \'forgiving\' to invalid input. I find myself writing involve

8条回答
  •  没有蜡笔的小新
    2020-12-12 16:36

    It depends on what exceptions you expect.

    If your only use case is get(), you could do

    item['b'] = myobject.get('key2', '')
    

    For the other cases, your decorator approach might be useful, but not in the way you do it.

    I'll try to show you:

    def f(func):
       def silenceit(*args, **kwargs): # takes all kinds of arguments
          try:
             return func(*args, **kwargs) # returns func's result
          except Exeption, e:
             print('Error:', e)
             return e # not the best way, maybe we'd better return None
                      # or a wrapper object containing e.
      return silenceit # on the correct level
    

    Nevertheless, f(some_undefined_function())won't work, because

    a) f() isn't yet active at the executon time and

    b) it is used wrong. The right way would be to wrap the function and then call it: f(function_to_wrap)().

    A "layer of lambda" would help here:

    wrapped_f = f(lambda: my_function())
    

    wraps a lambda function which in turn calls a non-existing function. Calling wrapped_f() leads to calling the wrapper which calls the lambda which tries to call my_function(). If this doesn't exist, the lambda raises an exception which is caught by the wrapper.

    This works because the name my_function is not executed at the time the lambda is defined, but when it is executed. And this execution is protected and wrapped by the function f() then. So the exception occurs inside the lambda and is propagated to the wrapping function provided by the decorator, which handles it gracefully.

    This move towards inside the lambda function doesn't work if you try to replace the lambda function with a wrapper like

    g = lambda function: lambda *a, **k: function(*a, **k)
    

    followed by a

    f(g(my_function))(arguments)
    

    because here the name resolution is "back at the surface": my_function cannot be resolved and this happens before g() or even f() are called. So it doesn't work.

    And if you try to do something like

    g(print)(x.get('fail'))
    

    it cannot work as well if you have no x, because g() protects print, not x.

    If you want to protect x here, you'll have to do

    value = f(lambda: x.get('fail'))
    

    because the wrapper provided by f() calls that lambda function which raises an exception which is then silenced.

提交回复
热议问题