Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does * work differently in assignment statements versus function calls?

In Python, varargs collection seems to work quite differently from how sequence unpacking works in assignment statements. I'm trying to understand the reason for this potentially confusing difference. I'm sure there is a good reason, but what is it?


# Example 1 with assignment statement
a, *b, c = 1,2,3,4
print(b) # [2, 3]

# Example 1 with function call
def foo(a, *b, c):
    print(b)
foo(1,2,3,4)

The function call results in the following error:

Traceback (most recent call last):
  File "<pyshell#309>", line 1, in <module>
    foo(1,2,3,4)
TypeError: foo() missing 1 required keyword-only argument: 'c'

Question 1: Why is b not assigned similar to how it is done in the assignment statement?

# Example 2 with function call
def foo(*x):
    print(x)
    print(type(x))
foo(1,2,3) # (1, 2, 3) <class 'tuple'>

# Example 2 with assignment statement
a, *b, c = 1,2,3,4,5
print(b) # [2, 3, 4]
print(type(b)) # <class 'list'>

Question 2: Why the difference in type (list vs tuple)?

like image 376
2020 Avatar asked Jun 25 '19 23:06

2020


People also ask

What is the relationship between an assignment statement and expression statement?

An assignment statement always has a single variable on the left hand side. The value of the expression (which can contain math operators and other variables) on the right of the = sign is stored in the variable on the left.

What are different types of assignment statements explain?

There are two types of assignment statements: Symbol assignment statements, which define or redefine a symbol in the symbol name space. Register assignment statements, which define or redefine a register name in the symbol name space.

How does assignment work in Python?

The assignment operator, denoted by the “=” symbol, is the operator that is used to assign values to variables in Python. The line x=1 takes the known value, 1, and assigns that value to the variable with name “x”. After executing this line, this number will be stored into this variable.


1 Answers

When used in an assignment, Python will try to make *b match to whatever it needs in order to make the assignment work (this is new in Python 3, see PEP 3132). These are both valid:

a, *b, c = 1,4
print(b) # []

a, *b, c = 1,2,3,4,5 
print(b) # [2, 3, 4]

When used in a function, if *b is the second parameter in the function definition, it will match with the second to last arguments in the function call, if there are any. It's used when you want your function to accept a variable number of parameters. Some examples:

def foo(a, *b):
    print(b)
foo(1) # ()
foo(1,2,3,4,5) # (2,3,4,5)

I recommend you read the following:

  • PEP 3132 -- Extended Iterable Unpacking

  • Understanding the asterisk(*) of Python.

  • Unpacking Argument Lists.

  • Keyword Arguments


On the difference between lists and tuples, the big one is mutability. Lists are mutable, tuples aren't. That means that this works:

myList = [1, 2, 3]
myList[1] = 4
print(myList) # [1, 4, 3]

And this doesn't:

myTuple = (1, 2, 3)
myTuple[1] = 4 # TypeError: 'tuple' object does not support item assignment

The reason why b is a list in this case:

a, *b, c = 1,2,3,4,5 
print(b) # [2, 3, 4]

And not a tuple (as is the case when using *args in a function), is because you'll probably want to do something with b after the assignment, so it's better to make it a list since lists are mutable. Making it a tuple instead of a list is one of the possible changes that were considered before this was accepted as a feature, as discussed in PEP 3132:

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.

like image 127
Ismael Padilla Avatar answered Oct 03 '22 10:10

Ismael Padilla