python function parameter

We, developers, write python programs every day for different purposes. However, there are some important scenarios and facts that are very simple but are avoided subconsciously. I will discuss some of the basic interesting topics about python function parameters and arguments that are sometimes confusing.

Function Parameters vs Function Arguments

When we define a python function, the inputs of the function are called parameters of the function but when we call python function() then the inputs are called the function arguments. For example,

def func(x,y):
    return x+y

Since we are defining a function, we call x and y as the function parameters. But when we call it,

func(a,b)

Here, a and b are the function arguments.

Positional Arguments

Positional arguments in a function reflect the position of the function parameters. The position must be in order.

For Example:

>> def add(a,b):
      return a+b

>> add(2,3) # a=2 and b=3
5

We can also pass a list as arguments, but in that case number of positional arguments should match the number of parameters of the function

>> to_add = [5,6] # Can pass a list but only of 2 elements
>> add(*to_add) # a=5 and b=6
11

Note that, * unpacks the elements of the list.

In order to take unlimited arguments in a function, function parameters look like this

>> def add_all(*args):
      print(args)
      return sum(args) 

Note that, we can use any name after *, for instance, I could write it like *anyname. Developers use *args for readability purposes.

Now, we can call the function like this

>> add_all(12,3,4,5,6,7)
(12, 3, 4, 5, 6, 7) 
37

We can pass the argument values to the function.

Here is an example,

def add(a,b,c):
     return a+b+c

add(c=3,b=1,a=0) 

Note that, the positional arguments should not follow keyword arguments. This means you must keep putting argument value once you start putting an argument value.

Now, let’s see the which one is correct and which one is incorrect:

FunctionIncorrect CallCorrect Call
my_func(2, b=3, 1)my_func(2, b=3, c=1)
my_func(a,b,c)my_func(a=2, b=3, 1)my_func(a=2, b=3, c=1)
my_func(c=2, 3, 1)my_func(c=2, b=3, a=1)
my_func(2, 3, 1)
Right way to assign positional argument values

This is also true for defining a function. After starting key arguments, no positional arguments are allowed. This happened when assigning default parameter values. This function is not allowed –

def add(a,b=3,c):
return a+b+c

However, this one is allowed –

def add(a,b=3,c=1):
    return a+b+c

Key Arguments in function (**kwargs)

We can tell Python function to allow multiple key-value pair arguments while calling the function. For this, we must use **kwargs parameter while defining the function. However, ** is important in this case. The kwargs name can be changed into any name you want. But conventionally, developers assume that kwargs means Key Word Arguments.

Let’s define a function with **kwargs,

>> def get_kwargs(**kwargs):
       print(kwargs)

>> get_kwargs(m=4, b=5)
{'m': 4, 'b': 5}

# While calling the function, we can also use a dictionary.
# We just need to unpack it

>> my_dict = dict(m=10, c=30)

>> get_kwargs(**my_dict) # ** unpacks a dictionary
{'m': 10, 'c': 30}

Notice that, the output of kwargs is actually a python dictionary. So, if we want to access the value of the parameter, we must access the dictionary using the keys.

However, When we define a function, no more positional or key parameters are allowed after using **kwargs.

Accept Positional Arguments and Key Arguments

Well, we can also tell a python function to allow both positional and and key arguments. Here is an example –

>> def get_all(*args,**kwargs):
       print("Positional Arguments: ", args)
       print("Key Arguments: ", kwargs)

>> get_all(1, 2, 4, m="Mahbub", n="Note")
Positional Arguments:  (1, 2, 4) 
Key Arguments:  {'m': 'Mahbub', 'n': 'Note'}

>> a, b = ['m','n'], dict(m="Mahbub",N="Note")

>> get_all(*a,**b) # Unpacking allowed
Positional Arguments:  ('m', 'n') 
Key Arguments:  {'m': 'Mahbub', 'N': 'Note'}

>> get_all(78,10, *a, v=10, **b) # This is also allowed
Positional Arguments:  (78, 10, 'm', 'n') 
Key Arguments:  {'v': 10, 'm': 'Mahbub', 'N': 'Note'}

We can also define a function parameters like this –

>> def get_all(a, b, *args, p, **kwargs):
       """
       a, b are mandatory positional arguments here.
       p is also mandatory but it is keyword-only argument.
       So you must tell the value of p while calling the function
       """
       print(a,b,p)
       print(args,kwargs)
       return "Allowed"


>> get_all(1,2,p=7)
1,2,7
() {} 
'Allowed'

We can conclude that *args and **kwargs are not mandatory while calling a function. They are always blank tuple and dictionary in this case. Positional arguments are mandatory, and keyword-only is also a mandatory argument.

Another interesting feature of python is, we can also tell the function not to accept anymore positional arguments after a certain position. We can use * as a parameter to do that. Here is an example –

>> def get_all(a, b, *args, *, p, **kwargs):
       return True

Here is some notes about the restrictions of python while defining parameters –

  • Positional arguments should not follow keyword arguments

  • We can not specify any positional argument parameter after the position *args while defining a function. So if we need any specific positional argument in the function parameter, we should put it before the position of *args in the function. Similarly, we can not specify any keyword parameter after the position of **kwargs, so they should be placed before the **kwargs while defining the function

  • *args and **kwargs take values up to the end as tuple and dictionary respectively.

Default Parameter and Heap

In many cases we assign default value of parameters in python function. It very useful and convenient. In most cases, developers use None as the default value of any parameter. None actually has an identical head memory. However, when we set default values of the parameters, they are stored in specific memory addresses while function compilation. When the values of the parameters are set, these values are overwritten.

If the parameter values are not given while calling the function, the default values are used without raising the errors.

However there is an issue with mutable data type like lists and dictionaries. We should not write default parameters for a list like this,

>> def total_cost(a, cost=[]):
       a+=1
       cost.append(a)
       return cost

>> a = total_cost(5)

>> a
[6]

>> b = total_cost(10)

>> b # Expectation [11]
[6, 11]

>> a # Expectation [6]
[6,11]

>> a is b # Are they using the same heap memory?
True

As you can see, assigning blank mutable value always uses the same memory address and function returns the value of the memory address to all the variables. That’s why all variables that received the value from the that function changes over time.

In this scenario, It is recommended to use None as the default value of that mutable parameter. However, we can also use this feature as a cache memory if required.

2 thoughts on “Python Function Parameters and Arguments

Leave a Reply

Your email address will not be published. Required fields are marked *