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

识别Python中引发异常的数据:如何收缩此代码?,python,exception-handling,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 "Da

我有一个脚本,它从检查错误数据的记录文件中读取。它们可能各自抛出相同的异常,并且它们存在于同一行中。有没有一种方法可以识别哪个字段抛出了异常,而不必将其拆分为多行

玩具示例如下:

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" % ("?")

当您捕捉到异常或异常时,您仍然拥有导致异常的信息 例外情况,例如:

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\u 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
最后一句话: 这不是真正的蟒蛇。相反,您可以使用:

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

正如评论中所说,
izip
izip_
值得研究

您可以将错误检查移动到
Fork
类中,并使用
itertools.izip_longest
确保在一个数据流短时间内传入/something/(实际上
):

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))

现在,您将得到一个错误,详细说明丢失了哪个数据元素。如果缺少的元素看起来更像是
,而不是什么都没有,那么您可以将
\uu init\uuu
中的测试从
If value is None
更改为
If not value

,在您介绍的情况下,您可以通过简单地检查
len(a)==len len len len(b)和len(b)==len len(c)来避免每次的错误检查
开头。将验证放入函数中,然后对每个项调用它。D.R.Y.@DanielFairhead我意识到,在写了这篇文章并编辑了它的代码以反映这一点之后。还是好奇有没有更短的路!你真的应该使用
izip
izip\u longest
而不是那种讨厌的索引。预验证可以简单到
assert len(set(map(len,(a,b,c)))==1
或更复杂的东西,这取决于您想要什么。知道我仍然在范围内很有用,但我想知道的是,是否有一种方法可以自动识别哪个变量缺少索引(例如:“a”),而不是索引本身,无需将其全部封装在函数中。感谢您使用此成语!
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))