如何在被调用程序中发生异常时将异常返回给调用程序,该程序有自己的异常处理?

时间:2017-08-23 08:31:22

标签: python exception-handling

我正在循环遍历python程序中的列表。对于列表中的每个项目,我从另一个python文件中调用一个函数,该文件在当前程序中导入。具有被调用函数的文件具有自己的异常处理。我希望调用程序在被调用程序中发生异常时跳过循环的当前迭代,并继续循环的下一次迭代。我尝试在调用程序的except子句中使用import B for item in list: try : B.some_function(item) except Exception as err: continue 关键字。但它并没有像我预期的那样发挥作用。

程序的结构是这样的 -

A.py

s = "{0} " \
    "{1} " \
    "{2}" \
    .format("Hello", "world", "from a multiline string")    
print(s)

请注意, B.py 文件包含多个具有自己的异常处理功能。上面的代码在第一个异常发生时终止。我想让它继续循环的下一次迭代。

为什么会这样?

3 个答案:

答案 0 :(得分:1)

听起来你需要AB之间的一些额外信号。它需要是B的透明添加,不会干扰它的当前行为。基本上向B的范围添加一些可以通过其异常处理修改的内容,并且可以由A查询。

取决于B中的其他内容 - 我认为模块级变量用作B中的try / except可能会更改的标记以及A中的try / except可以查询。

<强> B.py

#exception handling toggles this flag
exception_handled = False

def f():...
def g():...
...

<强> A.py

import B

for item in list:
    try : 
        B.some_function(item)
        if B.exception_handled:
            #reset flag
            B.exception_handled = False
            raise Exception
            # or just
            #continue   
    except Exception as err:
        B.exception_handled = False
        continue

或者您可以向B函数添加属性/标记,异常处理可以切换,A可以查询。

<强> B.py

#decorator to add flag to functions
def add_attr(func):
    func.exception_handled = False
    return func

@add_attr
def f():...
@add_attr
def g():...
...
#exception handler modifies a function attribute
#func.exception_handled = True

<强> A.py

import B

for item in list:
    try : 
        B.some_function(item)
        if B.some_function.exception_handled:
            # reset flag
            B.some_function.exception_handled = False
            raise Exception
            # or just
            #continue   
    except Exception as err:
        B.some_function.exception_handled = False
        continue

我想它可能会变得更加精致,如

B中的模块级字典,其中包含每个B函数的状态。

<强> B.py

exceptions = {}

def register(func):
    exceptions[func.__name__] = False
    return func

@register
def f():...
@register
def g():...

#exception handling -> exceptions[f] = True or exceptions[current_func] = True

然后A查询字典:

        if any(B.exceptions.values()):
            #reset the flags
            for k in B.exceptions:
                B.exceptions[k] = False
            #raise exception
            #just continue

对于最后两个解决方案,您可以使用sys,traceback和/或inspect模块确定函数名称。获得名称后,您可以根据需要通过globals()或locals()获取对函数本身的引用。以下是B.py中的内容的快速示例。

from pprint import pprint
import sys, inspect, traceback

exceptions = {}

def g():
    try:
        return 1 / 0
    except ZeroDivisionError as e:
        #find the function name as a string from the traceback
        typ, val, tb = sys.exc_info()
        # one way
        func_name0 = traceback.extract_tb(tb)[-1][-2]
        # another way via the frame object:
        # you have to get to the frame that caused the exception
        tb_x = tb
        while tb_x.tb_next is not None:
            tb_x = tb_x.tb_next
        func_name1 = tb_x.tb_frame.f_code.co_name
        print func_name0, func_name1
        # toggle the exeptions dictionary
        exceptions[func_name0] = True
        # get a reference to the function after finding the name
        func = globals()[func_name0]
        # and toggle its flag attribute
        func.exception_handled = True
        # what can you do with inspect
        # inspect.trace()[-1] contains the same frame object as tb_x
        pprint(inspect.trace()[-1])
        func_name2 = inspect.trace()[-1][-3]

从2.7代码的打印语句中可以看出,我在这台机器上没有3.x. 3.x中有一些新方法可用于追溯和检查可能有用的模块。

答案 1 :(得分:0)

如果B.some_function(item)从不引发单个异常,则try块无意义。 你永远无法发现B.some_function执行的任何故障。 因此,您需要B.some_function来实际引发错误。 所以,“为什么会这样?”只是因为“[some_function]有自己的异常处理”。

如果您有权访问其代码,则应对其进行编辑,以便在需要时实际引发错误。

如果您无法访问其代码,则有两种可能:

  1. 该功能的设计使得可以检测到错误或故障。在需要时引发(并且未捕获)错误,或者函数的返回值告知操作是否有效;
  2. 该功能的设计使其不会出现任何错误,并且无法检测到任何故障。
  3. 如果它是1,好的,你需要做的就是找到并理解如何检测错误。 如果它是2,那么,你不能(再次,假设你不能编辑代码)。 如果是这样,函数的使用方式可能与您尝试使用它的方式不同。

    由于您确实有权对some_function进行细微更改,因此这是一个不应该破坏任何依赖它的代码的修复程序,同时允许您检查是否发生了错误。

    首先,将关键字参数添加到B.some_function,默认为None。 这样,任何调用它的代码都不会中断,因为它将被设置而不被显式传递。 此参数将表现为标志:如果它被引发,则发生错误。 这里,“raise”表示其内部值设置为True。 如果没有标志作为参数传递,则行为保持不变。

    """B.py"""
    def some_function(arguments..., myFlag=None):
        # stuff
        if error_happens():
            if myFlag is not None:
                myFlag.value = True
        # stuff
    

    现在,在A.py中,定义一个标志类。 不能使用简单的布尔值,因为它需要通过引用传递给B.some_function,并由它进行修改。 MyFlag个实例具有value属性,初始化时设置为False,当标志需要发出事件发生时,将设置为True

    """A.py"""
    class MyFlag:
        def __init__(self):
            self.value = False
    
        # Just for more comfort: allows to write "if myFlag: ..."
        def __bool__(self):
            return self.value
    
    # Create a flag that will be down unless an error was raised
    errorCaught = MyFlag()
    # Call some_function and pass the flag as myFlag keyword argument
    B.some_function(arguments.., myFlag=errorCaught)
    # If errorCaught is True, then errorCaught.value is True as well,
    # hence an error was detected
    if errorCaught:
        print("An error got raised")
    

答案 2 :(得分:0)

在下面的示例中,我在Module B.py中定义了一个自定义异常 MyException ,并在函数some_function中引发了。在模块A.py中,我使用try和catch over调用b.py模块的some_function

<强> A.py

from B import some_function , MyException

lst = [0,2,4,1,3]   # for example take it as list of number

for item in lst:
   try : 
       some_function(item)
   except MyException as err:
       print "error"
       continue

<强> B.py

class MyException(Exception):
    pass

def some_function(val):
    """ this function raise exception when val equal to 1 """
    if val == 1:     # for example raise exception when input is 1
        raise MyException
    else:
        print val

<强>输出

0
2
4
error
3