我不建议使用这个,但是只是为了好玩,这里有一些代码可以实现你想要的功能。当你调用unpack(<sequence>)
时,unpack
函数使用inspect
模块找到函数被调用的源代码行,然后使用ast
模块解析该行并计算被拆包的变量数。
注意事项:
- 对于多重赋值(例如
(a, b) = c = unpack([1,2,3])
),它仅使用赋值语句中的第一个术语。
- 如果无法找到源代码(例如因为在repl中调用它),它将无法工作。
- 如果赋值语句跨越多行,则无法工作。
代码:
import inspect, ast
from itertools import islice, chain, cycle
def iter_n(iterator, n, default=None):
return islice(chain(iterator, cycle([default])), n)
def unpack(sequence, default=None):
stack = inspect.stack()
try:
frame = stack[1][0]
source = inspect.getsource(inspect.getmodule(frame)).splitlines()
line = source[frame.f_lineno-1].strip()
try:
tree = ast.parse(line, 'whatever', 'exec')
except SyntaxError:
return tuple(sequence)
exp = tree.body[0]
if not isinstance(exp, ast.Assign):
return tuple(sequence)
exp = exp.targets[0]
if not isinstance(exp, ast.Tuple):
return tuple(sequence)
n_items = len(exp.elts)
return tuple(iter_n(sequence, n_items, default))
finally:
del stack
if __name__ == '__main__':
x, y = unpack([1,2,3,4,5])
assert (x,y) == (1,2)
x, y, z = unpack([9])
assert (x, y, z) == (9, None, None)
x, y, z = unpack([1], 'foo')
assert (x, y, z) == (1, 'foo', 'foo')
assert unpack('abc') == ('a', 'b', 'c')
x = unpack([1,2,3])
assert x == (1,2,3)
x, = unpack([1,2,3])
assert x == 1
(x, y, z) = t = unpack('foobar')
assert (x, y, z) == t == ('f', 'o', 'o')
try:
t = (x, y, z) = unpack('foobar')
except ValueError as e:
assert str(e) == 'too many values to unpack'
else:
raise Exception("That should have failed.")
try:
(x, y, z) = unpack([
1, 2, 3, 4])
except ValueError as e:
assert str(e) == 'too many values to unpack'
else:
raise Exception("That should have failed.")