识别在Python中引发异常的数据:如何缩小此代码?

时间:2014-03-13 19:48:05

标签: python exception-handling

我有一个脚本,它从正在检查错误数据的记录文件中读取。他们可能会抛出相同的异常并且它们存在于同一行上。有没有办法确定哪个字段抛出异常而不必将其分成多行?

这里的玩具示例:

a = [1]
b = [2]
c = [] # Oh no, imagine something happened, like some data entry error
i = 0
try:
    z = a[i] + b[i] + c[i]
except IndexError, e:
    print "Data is missing! %s" % (str(e))

问题在于,如果有异常,则用户不知道a,b或c是否缺少数据。

我想我可以把它写成:

def check_data(data, index, message):
    try:
        return data[index]
    except IndexError, e:
        print "%s is missing." % (message)
        raise e

a = [1]
b = [2]
c = []
i = 0

try:
    z = check_data(a, i, "a") + check_data(b, i, "b") + check_data(c, i, "c")
except TypeError, e:
    print "Error! We're done."

但这可能非常繁琐。

我可以使用哪些其他方法来处理这种情况,以验证异常块中的每个字段(如果存在)?

以下现实改编的例子:

class Fork:
    def __init__(self, index, fork_name, fork_goal, fork_success):
        # In reality, we would do stuff here.
        pass


forks = []

# In reality, we'd be reading these in and not all of the entries might exist.
fork_names = ["MatrixSpoon", "Spoon", "Spork"]
fork_goals = ["Bend", "Drink soup", "Drink soup but also spear food"]
fork_success = ["Yes!", "Yes!"]

try:
    for i in range(0, len(fork_names)):
        forks.append(Fork(i + 1, fork_names[i], fork_goals[i], fork_success[i]))
except IndexError, e:
    print "There was a problem reading the forks! %s" % (e)
    print "The field that is missing is: %s" % ("?")

2 个答案:

答案 0 :(得分:1)

当您捕获或异常时,您仍然可以获得有关导致该异常的信息 异常,例如:

c_1 = None
try:
    c_1 = c[i]
except IndexError, e:
    print "c is missing."
    raise e # here you still have e and i

所以你可以这样做:

try:
    a = a_1[i]
except IndexError, e:
    raise Exception(e.message+'the violation is because of '+str(i))

更完整的解决方案......

如果您有兴趣了解导致违规行为的原因,例如:哪个列表有两个简短,你可以简单地对变量进行硬编码:

try:
    for i in range(0, len(fork_names)):
        forks.append(Fork(i + 1, fork_names[i], fork_goals[i], fork_success[i]))
except IndexError, e:
    print "There was a problem reading the forks! %s" % (e)
    print "There are fork_names with size %s " % len(fork_names)
    print "There are fork_goals with size %s " % len(fork_goals)
    print "There are fork_success with size %s " % len(fork_success)
    print "You tried accessing index %d" %  (i+1)

好的,我承认似乎做了很多工作!但这是值得的,因为你必须考虑你的输入并期待输出(TDD,如果你想......)。 但是这仍然很蹩脚,如果你不知道如何调用方法呢?有时你 会看到这个:

   def some_function(arg1, arg2, *args, **kwrds)
       pass

因此,您可以在异常中对内容进行硬编码,对于这种情况,您可以使用sys.exc_info打印堆栈信息:

try:
    for i in range(0, len(fork_names)):
        forks.append(Fork(i + 1, fork_names[i], fork_goals[i], fork_success[i]))
except IndexError, e:
    type, value, traceback = sys.exc_info()
    for k, v in traceback.tb_frame.f_locals.items():
        if isinstance(k, (list,tuple)):
            print k, " length ", len(k)
        else:
            print k, v

以上将输出

Fork __main__.Fork
traceback <traceback object at 0x7fe51c7ea998>
e list index out of range
__builtins__ <module '__builtin__' (built-in)>
__file__ teststo.py
fork_names ['MatrixSpoon', 'Spoon', 'Spork']
value list index out of range
__package__ None
sys <module 'sys' (built-in)>
i 2
fork_success ['Yes!', 'Yes!']
__name__ __main__
forks [<__main__.Fork instance at 0x7fe51c7ea908>, <__main__.Fork instance at 0x7fe51c7ea950>]
fork_goals ['Bend', 'Drink soup', 'Drink soup but also spear food']
type <type 'exceptions.IndexError'>
__doc__ None

因此,在检查上述跟踪后,您可以确定哪个列表太短。我承认,这就像拍摄调试器一样。所以,如果你想,这里是如何拍摄调试器:

try:
   some_thing_that_fails()
except Exception:
   import pdb; pdb.set_trace() 
   # if you want a better debugger that supports autocomplete and tab pressing
   # to explore objects you should you use ipdb
   # import ipdb; ipdb.set_trace()

最后评论:

 for i in range(0, len(fork_names)) 

这不是真正的Pythonic。相反,你可以使用:

 for idx, item enumerate(fork_names):
     forks.append(Fork(idx + 1, fork_names[idx], fork_goals[idx], fork_success[idx]))

就像评论中所说,izipizip_longest值得研究。

答案 1 :(得分:1)

您可以将错误检查移至Fork类,并使用itertools.izip_longest确保在一个数据流短缺时传入/ something /(真正None):

class Fork:
    def __init__(self, index, fork_name, fork_goal, fork_success):
        # first, check parameters
        for name, value in (
                ('fork_name', fork_name),
                ('fork_goal', fork_goal),
                ('fork_success', fork_success)
            ):
            if value is None:
                raise ValueError('%s not specified' % name)
        # rest of code

forks = []

# In reality, we'd be reading these in and not all of the entries might exist.
fork_names = ["MatrixSpoon", "Spoon", "Spork"]
fork_goals = ["Bend", "Drink soup", "Drink soup but also spear food"]
fork_success = ["Yes!", "Yes!"]

然后改变你的循环:

for name, goal, sucess in izip_longest(fork_names, fork_goals, fork_success):
    forks.append(Fork(names, goal, success))

现在,您将收到一个错误,清楚地详细说明缺少哪个数据元素。如果您的缺失元素看起来更像'',那么您可以将__init__中的测试从if value is None更改为if not value