开发者

excess positional arguments, unpacking argument lists or tuples, and extended iterable unpacking

This question is going to be rather long, so I apologize preemptively.

In Python we can use * in the following three cases:

I. When defining a function that we want to be callable with an arbitrary number of arguments, such as in this example:

def write_multiple_items(file, separator, *args):
    file.write(separator.join(args))

In this case, the excess positional arguments are collected into a tuple.

II. The reverse case is when the arguments are already in either a list or a tuple and we wish to unpack them for a function call requiring separate positional arguments, such as in this example:

>>> range(3, 6)             # normal call with separate arguments
[3, 4, 5]
>>> args = [3, 6]
>>> range(*args)            # call with arguments unpacked from a list
[3, 4, 5]

III. Starting with Python 3, * is also used in the context of extended list or tuple unpacking, such as in this example for tuples:

>>> a, *b, c = range(5)
>>> b
[1, 2, 3]

or for lists:

>>> [a, *b, c] = range(5)
>>开发者_JAVA技巧;> b
[1, 2, 3]

In both cases, all items from the iterable being unpacked that are not assigned to any of the mandatory expressions are assigned to a list.

So here's the question: in case I the extra args are collected into a tuple, while in case III the extra items are assigned to a list. Whence this discrepancy? The only explanation I could find was in PEP 3132 which says that:

Possible changes discussed were:

[...]

Make the starred target a tuple instead of a list. This would be consistent with a function's *args, but make further processing of the result harder.

However, from a pedagogical perspective this lack of consistency is problematic, especially given that if you wanted to process the result, you could always say list(b) (assuming b in the above examples was a tuple). Am I missing something?


You missed one.

IV. Also, in Python 3, a bare * in the argument list marks the end of positional arguments, allowing for keyword-only arguments.

def foo(a, b, *, key = None):
    pass

This can be called foo(1, 2, key = 3) but not foo(1, 2, 3).


In Python we can use * in the following three cases:

You mean prefix * , of course -- infix * is used for multiplication.

However, from a pedagogical perspective this lack of consistency is problematic, especially given that if you wanted to process the result, you could always say list(b) (assuming b in the above examples was a tuple). Am I missing something?

I would say that the design problem (old and very long in the tooth!) is with the fact that when you're receiving arbitrary arguments you're getting them as a tuple, when a list would be more useful in many cases with no real downside (the tiny amount of extra processing and memory that may be needed to make a list instead of a tuple is negligible in the context of function call overhead -- or sequence unpacking, for that matter; the extra processing and memory needed to make a list as well as a tuple is really more annoying).

There's very little that you can do with a tuple but not a list -- basically, just hashing it (to use as a set item or dict key) -- while a list offers much more extra functionality, and not just for purposes of altering it... methods such as count and index are also useful.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜