Up to this point, we have been calling functions mainly with positional arguments.
But in Python, functions can also have named or keyword arguments. What does that mean? Let us find out by looking at the following example:
def bar(length, char1, char2):
return (char1 + char2) * length + char1
print(bar(5, '-', '*'))
# => -*-*-*-*-*-
The bar
function has three arguments. The function call also looks easy. Let us assume that char1
and char2
often get the same values. In such a situation, it is convenient to specify default values:
def bar(length, char1='-', char2='*'):
return (char1 + char2) * length + char1
print(bar(5))
# => -*-*-*-*-*-
print(bar(3, '.'))
# => .*.*.*.
print(bar(2, ':', '|'))
# => :|:|:
It looks convenient. But what if we are satisfied with the default value for char1
and set our value for char2
? Will we have to specify both? You do not have to do it unless there is a rare exception.
We can specify any positional argument by name:
print(bar(4, char2='#'))
# => -#-#-#-#-
print(bar(char2='$', length=3))
# => -$-$-$-
In the second call, we even messed up the order of the arguments, and nothing went wrong. Yes, named arguments have a property: the order of named arguments does not matter.
However, the order of groups of arguments matters. We should specify positional values before any named ones. Otherwise, you will get SyntaxError: positional argument follows keyword argument
.
Also, when a function has positional arguments without default values, the values for these arguments must be specified one way or another, either as positional or named arguments. Violating this rule will give you this error:
bar(char2='!')
# TypeError: `bar()` missing 1 required positional argument: 'length'
We should distinguish between the syntax of the declaration of the function arguments and the syntax of the calls. When calling a function, you have more freedom. For example, you can specify named arguments before the expanded group of positional ones:
def f(*args, x=None, y=None):
print('args =', args, ', x =', x, ', y =', y)
f(*(1, 2), x='a', *[3, 4], y='b', *(5, 6))
# => args = (1, 2, 3, 4, 5, 6), x = a, y = b
When to use named arguments
There are no strict rules in this regard. However, this approach is popular: if a function accepts more than three arguments, we should specify at least some by name. It is essential to name argument values if several values have the same type because it is difficult to understand what a function with a call like this does:
make('circle', 300, 150, 10, None, 2.5, False)
Compare it with this:
make(
shape='circle',
x=300, y=150, radius=10,
line_pattern=None,
line_width=2.5,
fill=False
)
This code is much easier to read. Of course, the above rule has a couple of exceptions. Firstly, functions whose purpose is obvious. Yes, the evidence is relative, but it is usually easy to understand what is behind the values in the call:
point3d(10, 50,21)
rgb(0, 255, 0)
Here, alas, only common sense works.
Secondly, it makes no sense, nor is it possible, to specify the arguments declared with an asterisk by name:
def sum(*args):
…
sum(x1=1, x2=2)
# TypeError: `sum()` got an unexpected keyword argument: 'x1'
In these functions, the name args
is inaccessible from the outside because it is not an argument but a vehicle for any number of them.
Are there any more questions? Ask them in the Discussion section.
The Hexlet support team or other students will answer you.
For full access to the course you need a professional subscription.
A professional subscription will give you full access to all Hexlet courses, projects and lifetime access to the theory of lessons learned. You can cancel your subscription at any time.