关于python:检查变量是否可以解压缩

Checking if a variable can be unpacked

假设我有一个变量x,它的数据类型未知。我还有一些随机函数foo。现在,我想沿着以下几行做一些事情:

如果x是可以使用**解包的类型,例如字典,则称为foo(**x)。否则,如果x是可以使用*解包的类型,例如元组,则调用foo(*x)。否则,打电话给foo(x)

是否有一种简单的方法来检查类型是否可以通过**或*?

我目前正在做的是检查x的类型并执行如下操作:

1
2
3
4
5
6
if type(x) == 'dict':
    foo(**x)
elif type(x) in ['tuple','list', ...]:
    foo(*x)
else:
    foo(x)

但问题是,我不知道实际可以解包的数据类型的完整列表,我也不确定用户定义的数据类型是否可以有一个允许解包的方法。


您可以使用try

1
2
3
4
5
6
7
try:
    foo(**x)
except:
    try:
        foo(*x)
    except:
        foo(x)

它是一种粗糙的类型,不区分异常发生的原因(可以通过检查异常类型来减轻),但消除了尝试并枚举可以以何种方式调用的类型的需要。


让我们检查一下在做得不好时收到的错误:

1
2
3
4
5
6
7
8
9
>>> x = 1
>>> f(*x)
Traceback (most recent call last):
  File"<stdin>", line 1, in <module>
TypeError: f() argument after * must be a sequence, not int
>>> f(**x)
Traceback (most recent call last):
  File"<stdin>", line 1, in <module>
TypeError: f() argument after ** must be a mapping, not int

太好了:所以我们需要一个用于*的序列类型和一个用于**的映射类型。其余部分相当简单:python 3文档的状态是:

There are three basic sequence types: lists, tuples, and range objects. Additional sequence types tailored for processing of binary data and text strings are described in dedicated sections.

检查var是否为序列类型的故障安全方法是:

1
2
3
>>> import collections
>>> all(isinstance(x, collections.Sequence) for x in [[], (), 'foo', b'bar', range(3)])
True

(有关详细信息,请参见python:检查对象是否为序列)

根据文档,映射类型为dict

There is currently only one standard mapping type, the dictionary.

您可以用同样的方法检查它,使用isinstance,它甚至可以处理派生类:

1
2
3
4
>>> from collections import OrderedDict
>>> from collections import Counter
>>> all(isinstance(x, dict) for x in [{}, OrderedDict(), Counter()])
True

所以你可以这样做:

1
2
3
4
5
6
7
import collections
if isinstance(x, dict):
    foo(**x)
elif isinstance(x, collections.Sequence):
    foo(*x)
else:
    foo(x)