我的元组子类的用法:
class MyTuple(tuple):
def __new__(cls, columns=()):
return tuple.__new__(cls, tuple(columns))
a, b = MyTuple(columns=('hello', 'world', 42))
给出以下例外:
Traceback (most recent call last):
File "<stdin>", line 6, in <module>
ValueError: too many values to unpack
ValueError
可能有很多原因,所以我需要使用以下内容来捕捉它吗?
try:
a, b = MyTuple(columns=('hello', 'world', 42))
except ValueError as e:
if not str(e).endswith('unpack'):
raise # false alarm
# handle unpacking error here..
这似乎相当不优雅..有没有办法可以覆盖元组拆包引发的异常?
更新:实际用例如下
>>> dice = FactSet()
>>> for i in range(1, 7):
... dice.add('dice', n=i)
...
>>> print dice.n + dice.n == 10 # give me all combinations that add up to 10
XPROD((dice(n=4), dice(n=6))
(dice(n=5), dice(n=5))
(dice(n=6), dice(n=4)))
>>> a, b = dice.n + dice.n == 10 # same as above, but unpack the individual die
>>> a
FactSet([
dice(n=4),
dice(n=5),
dice(n=6),
])
>>> b
FactSet([
dice(n=6),
dice(n=5),
dice(n=4),
])
>>> a, b = dice.n + dice.n == 13 # no result should probably raise a more specific exception?
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: need more than 0 values to unpack
>>> ab = dice.n + dice.n == 13 # this is much more awkward and forces you to deal with the error in-situ (which might not be what you wanted)
>>> if ab:
>>> a, b = ab
答案 0 :(得分:6)
而不是:
a, b = <expression>
执行:
value = <expression>
try:
a, b = value
except ValueError:
...
这是解决此类问题的一般方法:当您的陈述可能由于某种原因而引发相同类型的异常时,只需将该陈述分成多个部分,即可隔离&#34; critical&#34;部分来自其余的代码。
有没有办法可以覆盖元组解包引发的异常?
没有。 tuple
对象不会引发异常,它由Python解释器引发。执行:a, b, ..., z = something
时,解释器在幕后执行以下代码:
it = iter(something)
try:
a = next(it)
b = next(it)
...
z = next(it)
except StopIteration:
raise ValueError('need more than X values to unpack')
try:
next(it)
except StopIteration:
pass
else:
raise ValueError('too many values to unpack (expected Y)')
正如您所看到的,元组(或一般情况下的可迭代)仅被迭代。它不知道发生了什么。
正如您通过阅读CPython's source code所看到的那样,这些例外情况是硬编码的,并且不能被覆盖&#34;。
答案 1 :(得分:1)
根据@ AndreaCorbellini的回答,这是一个自定义迭代器实现,具有合理/可调整的语义:
import collections
class MyTupleIter(collections.Iterator):
def __init__(self, mt, length):
self.mt = mt
self.pos = -1
self.length = length
def next(self):
self.pos += 1
if self.pos < self.length:
if self.pos >= len(self.mt):
return 'empty-value'
return self.mt[self.pos]
raise StopIteration
class MyTuple(tuple):
def __new__(cls, columns=()):
return tuple.__new__(cls, tuple(columns))
def __iter__(self):
return MyTupleIter(self, length=2)
a, b = MyTuple()
print a, b # empty-value empty-value
a, b = MyTuple(columns=(1,2,3))
print a, b # 1 2