DEV Community

Cover image for Python3 Programming - Exercise 12 b - Function that returns a value
Otu Michael
Otu Michael

Posted on • Updated on

Python3 Programming - Exercise 12 b - Function that returns a value

This is a continuation of exercise 12 a (A function and its definition)

Function that returns a value

So far, we have written functions that print values to the screen. We can also return a value from a function, assign that value to a variable and make use of it in the future.

Example 1

Previously when we wrote the calc_area function, it prints out a string, detailing the input and the output. Say, we are only interested in the computed result, we can return the computed result using the return keyword followed by the computed value. The snippet below is only modified to return the computed by calc_area.

def calc_area(base, height):
    return 0.5 * base * height


# take the base and height from the user
base = float(input("Enter base: "))
height = float(input("Enter height: "))

# call the function here and pass base and height
# to it as the base and height are required
# the value is then assigned to a variable, `area`
area = calc_area(base, height)

# We then make use of the `area`
print(
    f"The area of a triangle of base, {base} and height, {height} is {area}")

Enter fullscreen mode Exit fullscreen mode

calc_area is now not associated with printing a user-friendly text, telling what input and output were involved. This is left in the hands of the user to decide. This makes it easier to write unit tests for calc_area and also import it as a script in other programs.

Example 2

This is a function that takes a list as an argument and then sorts it from the lowest to the highest. How does this sorting function work?

Say that we are given a list of size, n, numbers, using two loops, nested actually. The first loop loops through the list n - 1 times. The second does, n - 2 times. In the second loop, we check if the first value is greater than the second value. If it is, we swap the values and then we compare the second with the third and then the third and fourth and so on until we reach the end of the list. The first loop moves (i.e steps 1) then the process starts again.

def sort_func(nums):
    """
    This function takes a list as an argument and
    returns a sorted version of it
    """
    n = len(nums)

    for i in range(n):
        for j in range(1, n):
            if nums[j - 1] > nums[j]:
                # swap
                nums[j - 1], nums[j] = nums[j], nums[j - 1]

    return nums


print(sort_func([6, 3, 2, 4, 1]))
print(sort_func(['w', 't', 'a', 'i']))

Enter fullscreen mode Exit fullscreen mode

On this line, nums[j - 1], nums[j] = nums[j], nums[j - 1], we swap values here if nums[j - 1] > nums[j]. This is a "technique" used for swapping in python without introducing an helping variable.

We could have done,

temp = nums[j - 1]
nums[j - 1] = nums[j]
nums[j] = temp
Enter fullscreen mode Exit fullscreen mode

Function with many arguments

We have passed single value (data) or a list into our function as arguments so far. Now, say we would like to pass several arguments into a function (we can still use a list - sort of), we are forced to give the function numerous parameters on creation but there is a simple approach in Python.

Example 3

A function that takes several strings as an argument and prints the number of characters in each argument.

def many_args(s1, s2, s3, s4, s5):
    print(len(s1))
    print(len(s2))
    print(len(s3))
    print(len(s4))
    print(len(s5))

many_args('sandy', 'jude', 'mani', 'desmond', 'peter')
Enter fullscreen mode Exit fullscreen mode

We did it right? No. What if we want to pass a sixth argument? We have to update the many_args function. Any other program that has used many_args function has to update the number of arguments passed. We don't want that. We should never write such functions. This is an opinion and a style choice.

*args and **kwargs

In the next exercise we shall discuss more on data structures in python such as list, dictionary, set and tuple into some details. These data structures help in organization and management of data. We came across the word "iterable". The previously mentioned data structures also allow iteration.

list, tuple, set and dictionary in brief

In this context, we are interested in data structures as arguments so these should be enough.

  • A list is a comma-separated collection of data delimited by square brackets, [data1, data2, ...] where data can be of any data type or data structure. This implies we can have a list that has other lists as data.
  • A tuple is just list but is delimited by brackets, (data1, data2, ...)
  • A set is just like a list but without duplicates, delimited by curly brackets, {data1, data2, ...}.
  • A dictionary is a key-name pair kind of data structure. So far, list, tuple and set are indexed (enumerated) from 0 to n - 1, where n is the size. For a dictionary, we use a key to access a value. {key1:value2, key2:value2, ...}. We can loop through the keys, the values and the keys and values. A dictionary is more like a set, with uniqueness in the keys.

*args

We discussed passing multiple data into a function as arguments and we saw some of the limitations.

*args means a collection of data, such as list, tuple or set. The parameter name, *args, is a conversion. The variable name, args, is preceded by an asterisk, *. It is the * that makes the variable a collection of data. So we can use any name as far as it is preceded by an asterisk, *. *pancakes works fine as *args.

The number of data in the variable is unspecified. This solves the issue we had - we had to update the function when we wanted to pass another data to the function.

One of the key concepts here is, limiting the number of parameters a function takes. No more, func(arg1, arg2, arg3, ...). At most four and these four should be named arguments if something like that happens.

Example 4

We shall we implement example 3 but this time using the *args concept.

def many_args(*s):
    for i in s:
        print(i, "=", len(i))


many_args(
    'sandy', 'jude', 'mani', 'desmond', 'peter',
    'milo', 'john', 'kobby', 'hannah', 'alvin',
    'amira', 'leticia', 'sadik', 'ed', 'kwesi')

Enter fullscreen mode Exit fullscreen mode

We were able to pass several arguments to many_args but our function just has *s as a parameter.

Passing a list into a function that takes *args

We can also pass a list into many_args

largs = ['candy', 'foo', 'bar', 'swift', 'python',
         'ryder', 'horse', 'rubber-duck', 'pizza']


many_args(largs)
Enter fullscreen mode Exit fullscreen mode

But what was the output? Try it out and see. What do you think happened (Look at example 4)? How do we fix this?

making a list argument as *list_arg

This 'candy', 'foo', 'bar', 'swift', 'python', 'ryder', 'horse', 'rubber-duck', 'pizza'] = 9, was the output from Example 5. What this means is that we passed one argument to many_args and this argument is a list of nine strings. We did not pass nine strings as an argument to many_args.

To pass a list or tuple as *args, we only have to precede the variable name by an asterisk. Try it out and see for yourself.

many_args(*largs)


# output of *largs
# candy = 5
# foo = 3
# bar = 3
# swift = 5
# python = 6
# ryder = 5
# horse = 5
# rubber-duck = 11
# pizza = 5

Enter fullscreen mode Exit fullscreen mode

**kwarg

**kwarg stands for keyword arguments. **kwarg is basically *args, but we pass keys alongside the values. So the values are accessed by the keys. This is sounds like a dictionary, right?.

Example 5

def many_args(**s):

    for key, value in s.items():
        print(f"{key}: {value}, has {len(value)} chars")


many_args(title="Swift python", author="Otu Michael",
          hobby="Staring at the screen")

Enter fullscreen mode Exit fullscreen mode

Now we can access the values passed, with their keys.

passing a dictionary as data

We said lamely, that a dictionary is a key-name pair data structure. Consider the snippet below, where we create a dictionary.

dict_data = {
    'title': "Swift python",
    'author': "Otu Michael",
    'hobby': "Staring at the screen"
}
Enter fullscreen mode Exit fullscreen mode

Let's pass **dict_data into many_args

many_args(**dict_data)

# output
# title: Swift python, has 12 chars
# author: Otu Michael, has 11 chars
# hobby: Staring at the screen, has 21 chars
Enter fullscreen mode Exit fullscreen mode

What will happen when ** is omitted? Try it out. Does adding a key, solve the issue? What did you notice?

Default arguments

So what do you think would happen when we don't pass an argument to the function that takes an argument?

Of course an error. We want our functions to "function" with default data so that the function will assume (use) this value when none is passed.

Example 6

Let's make use of the calc_area function. We would assign a default value to the parameters.

def calc_area(base=1, height=1):
    return 0.5 * base * height


# passed no args
print(calc_area())  # 0.5

# passed base but not height
print(calc_area(base=12))  # 0.6

# passed height but not base
print(calc_area(height=14))  # 0.7

Enter fullscreen mode Exit fullscreen mode

Here the default parameters are 1.

Note

  • You can do some_name = func_name then call, some_name(). This will work just like func_name()

Discussion (0)