4

The Python 3 tutorial about the unpacking operator (*) generically speaks of a "list or tuple," while the error message for improper use says that a "sequence" is needed:

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's Built-in types documentation lists the following sequence types:

  • Sequence Types — list, tuple, range
  • Text Sequence Type — str
  • Binary Sequence Types — bytes, bytearray, memoryview

Quick test:

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

Note that set types (like set and frozenset) and mapping types (dict) are not included here.

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

My question: Why are all iterable types (including a set or dict) unpackable? They are not sequence types, as the TypeError above suggests they should be, and unordered behavior leads to undefined results when unpacking for positional args:

>>> 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
8
  • Pretty sure only iterator methods are required for that. Unpacking predates abstract collection types. Commented Oct 28, 2016 at 16:10
  • 2
    The error message in Python 3.5 is TypeError: foo() argument after * must be an iterable, not int Commented Oct 28, 2016 at 16:15
  • 1
    So, basically you ask Why is the order in dictionaries and sets arbitrary? Commented Oct 28, 2016 at 16:20
  • 1
    @ŁukaszRogalski No--my question is why unordered collections allow unpacking, given the error message and unexpected behavior with positional args. I understand that they are arbitrary. Commented Oct 28, 2016 at 16:21
  • 1
    I guess the issue with the error message is "solved", but something still doesn't feel completely right about being able to unpack positional arguments from an unordered collection. (No doubt this is why the error message originally mentioned sequences; as that would, at least naively, make the most sense. And maybe it was even true in very early versions of Python.) Commented Oct 31, 2016 at 22:50

2 Answers 2

4

This is no longer the case in (at least) Python 3.5.2 -- Probably was recognized as an issue and changed in a version later than what you're using. The message now more appropriately reads iterable instead of sequence

See https://bugs.python.org/issue4806

>>> def foo(*args):
...     print(*args)
...
>>> foo(*1)
TypeError: foo() argument after * must be an iterable, not int
Sign up to request clarification or add additional context in comments.

2 Comments

Interesting: I'm using v3.5.1:37a07cee5969
I'm on 3.5.2 -- specifically v3.5.2:4def2a2901a5
2

The error message is most likely a little bug*. Anything that is an iterable is accepted during function calls; this is hidden inside the 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.

(Emphasis mine)

*Which, as of 3.5.2 as @sytech pointed out, was fixed in Issue 4806 to correspond to the correct wording in the reference manual.

2 Comments

A bug that, evidently, has been addressed. I'm on 3.5.2 and the message actually says iterable not sequence
@sytech Right, pointed that out, any luck finding that bug report yet?

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.