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.
精彩评论