Arguments, as pieces of information passed to a function provide a way to insert values into function’s mechanism. In Python, function arguments directly depend on object reference model, as there is a stark difference in behavior of immutable and mutable arguments. At this point, a linguistical remark is needed to differentiate between argument and parameter: although those two terms are usually used interchangeably, strictly speaking, when declaring function we use parameters, while upon a function’s call we pass arguments.
Python uses number of matching rules when differentiating passed arguments. Those rules are, as follows:
- positional arguments are matched from left to right. This is the most widely used case, for example:
1 2 3 4 5
def func(a,b,c): print((a+c)/b) func(3,9,12) # 1.6666666666666667 func(3,12,9) # 1.0
- keyword arguments are matched by given name, instead of position. For instance:
1 2 3 4 5
def func(b=20,a=5,c=22): print(a,b,c) func() # 5 20 22 func(3,12,9) # 12 3 9
- arguments with defaults specify default values if the function’s call doesn’t contain all specified arguments (parameters)
1 2 3 4 5
def func(a, b=10, c=20): print(a,b,c) func(12) # 12 10 20 func(12, 15, 22) # 12 15 22
- star expressions (varargs): arguments preceeded with * or ** characters to collect an arbitrary number of arguments
1 2 3 4 5
def func(*args): print(args) func(10) # 10 func(1,2,3,4) # 1,2,3,4
** feature works for keywords and collect them into a new dictionary:
1 2 3 4
def func(**args): print(args) func(x=5, y=10) # {'x': 5, 'y': 10}
varargs unpacking. Passing arbitrarily number of positional or keyword arguments
1 2 3 4 5 6 7
def func(a,b,c,d): print(a,b,c,d) args =(0,1) args+=(3,5) func(*args) # 0 1 3 5
Using the same function but providing a dictionary instead and **:
1 2 3 4 5 6
def func(a,b,c,d): print(a,b,c,d) args={'a':0, 'b':5, 'c':10, 'd':15} func(**args) # 0 1 3 5
- keyword only: arguments that must be passed by name
1 2 3 4 5
def func(a, *b, c): print(a, b, c) func(1,2,3) """ TypeError: func() missing 1 required keyword-only argument: 'c'""" func(1,2,c=3) #1 (2,) 3
Now let’s explain difference between immutable and mutable arguments. Consider the following code:
1
2
3
4
5
6
def func(a):
a=10
b=20
func(b)
print(b) # 20
Here the variable a is assigned the object with the value od 20 on the function’s call. Although inside a function’s definition the object a changes to 10, changes don’t have effect on function’s call as they only reset the local variable to another object. In contrast with C/C++, Python lacks aliasing; meaning that assignment to a parameter name defined in function (a=10) does not change variable b upon the function’s call. Since arguments are, in fact, pointers to the objects, they can’t share same objects permanently – when it comes to immutables (see this article).
However, when mutable objects (lists, dictionaries, class objects) are passed as arguments, in-place changes will probably occur, impacting the callers as well. For instance:
1
2
3
4
5
6
7
8
def func(x,y):
x=5
y[0]="test"
X=1
C=[2,4]
func(X,C)
print(X,C) # 1 ['test', 4]
In the upper code, the func function assigns values to a immutable and mutable variables. Since x is a local variable in the function’s code scope, x=5 has no effect upon function’s call as previously explained – all it does is changes the local variable a to another’s object reference. Y is a local variable but since the list is the mutable object (see explanation), statement y[0]=”test” is the in-place change which impacts the value of the corresponding list’s element. This is the reason why this change outlives the function’s call and impacts the list C. If those in-place changes are not wanted, we could copy the list so the original one isn’t affected:
1
2
3
4
def func(x,y):
x=5
y=y[:]
y[0]="test"
Summarizing Python’s argument matching rules, here is the complete list of possible cases:
- def func(name): matches arguments by position or name
- def func(name=value): defines default argument value (if not passed in function’s call)
- def func(*name): matches and collects remaining, possibly multiple, positional arguments in a tuple
- def func(**name): matches and collects remaining, possibly multiple, keyword arguments in a dictionary
- def func(*other, name): defines arguments that must be passed in calls by keywords
- func(value): matching by position in function’s call
- func(name=value): matching by name in function’s call
- func(*iterable): pass all objects in iterable as individual positional arguments
- func(**dict): pass all key/value pairs in dict as individual keyword arguments