为什么 Python 解包运算符适用于无序集合
Why does Python unpacking operator work with unordered collections
关于解包运算符 (*
) 的 Python 3 tutorial 一般说的是 "list or tuple," 而错误消息表示使用不当需要 "sequence":
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 的 Built-in types 文档列出了以下序列类型:
- 序列类型 —
list
、tuple
、range
- 文本序列类型 —
str
- 二进制序列类型 —
bytes
、bytearray
、memoryview
快速测试:
>>> all(isinstance(x, collections.Sequence) for x in [[], (), range(1), '', b''])
True
请注意,集合类型(如 set
和 frozenset
)和映射类型 (dict
) 不 包含在此处。
>>> any(isinstance(x, collections.Sequence) for x in [set(), {}])
False
我的问题:为什么所有可迭代类型(包括 set
或 dict
)都是不可压缩的? 它们不是序列类型,因为 [=上面的 28=] 表明它们应该是,并且在为位置参数解包时,无序行为会导致未定义的结果:
>>> 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
错误信息很可能是一个小错误*。在函数调用期间接受任何可迭代对象;这隐藏在 section for Calls in the Python Reference Manual:
中
If the syntax *expression
appears in the function call, expression must evaluate to an iterable. Elements from these iterables are treated as if they were additional positional arguments.
(强调我的)
*正如@sytech 指出的那样,从 3.5.2
开始,它已在 Issue 4806
中得到修复,以对应参考手册中的正确措辞。
在(至少)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
关于解包运算符 (*
) 的 Python 3 tutorial 一般说的是 "list or tuple," 而错误消息表示使用不当需要 "sequence":
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 的 Built-in types 文档列出了以下序列类型:
- 序列类型 —
list
、tuple
、range
- 文本序列类型 —
str
- 二进制序列类型 —
bytes
、bytearray
、memoryview
快速测试:
>>> all(isinstance(x, collections.Sequence) for x in [[], (), range(1), '', b''])
True
请注意,集合类型(如 set
和 frozenset
)和映射类型 (dict
) 不 包含在此处。
>>> any(isinstance(x, collections.Sequence) for x in [set(), {}])
False
我的问题:为什么所有可迭代类型(包括 set
或 dict
)都是不可压缩的? 它们不是序列类型,因为 [=上面的 28=] 表明它们应该是,并且在为位置参数解包时,无序行为会导致未定义的结果:
>>> 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
错误信息很可能是一个小错误*。在函数调用期间接受任何可迭代对象;这隐藏在 section for Calls in the Python Reference Manual:
中If the syntax
*expression
appears in the function call, expression must evaluate to an iterable. Elements from these iterables are treated as if they were additional positional arguments.
(强调我的)
*正如@sytech 指出的那样,从 3.5.2
开始,它已在 Issue 4806
中得到修复,以对应参考手册中的正确措辞。
在(至少)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