4

关于解包运算符 ( )的Python 3 教程*一般说的是“列表或元组”,而错误使用的错误消息说需要一个“序列”:

Python 3.5.1 (v3.5.1:37a07cee5969, Dec  6 2015, 01:38:48) [MSC v.1900 32 bit (Intel)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> def f(a, b):
...     return a / b
...
>>> f(*1)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: f() argument after * must be a sequence, not int

Python 3 的内置类型文档列出了以下序列类型:

  • 序列类型 - list, tuple,range
  • 文本序列类型 —str
  • 二进制序列类型 - bytes, bytearray,memoryview

快速测试:

>>> all(isinstance(x, collections.Sequence) for x in [[], (), range(1), '', b''])
True

请注意,此处包括集合类型(如setfrozenset)和映射类型(dict) 。

>>> any(isinstance(x, collections.Sequence) for x in [set(), {}])
False

我的问题:为什么所有可迭代类型(包括 asetdict)都是不可打包的?它们不是序列类型,正如TypeError上面所暗示的那样,当为位置参数解包时,无序行为会导致未定义的结果:

>>> def f(a, b):
...     return a / b
...
>>> f(*{4, 2})
0.5
>>> f(*{8, 2})
4.0
>>> f(*{4:1, 2:1})
0.5
>>> f(*{2:1, 8:1})
4.0
4

2 回答 2

4

在(至少)Python 3.5.2 中不再是这种情况——可能被认为是一个问题,并在比您使用的版本更高的版本中进行了更改。消息现在更合适地读取iterable而不是sequence

https://bugs.python.org/issue4806

>>> def foo(*args):
...     print(*args)
...
>>> foo(*1)
TypeError: foo() argument after * must be an iterable, not int
于 2016-10-28T16:17:33.413 回答
2

错误消息很可能是一个小错误*。在函数调用期间接受任何可迭代的内容;这隐藏在Python 参考手册中的调用部分中

如果语法*expression出现在函数调用中,则表达式必须计算为可迭代的。这些迭代中的元素被视为附加的位置参数。

(强调我的)

*3.5.2正如@sytech 所指出的,已修复Issue 4806以对应参考手册中的正确措辞。

于 2016-10-28T16:16:06.543 回答