Functions

What are Variables and Data Types?

Arguments
In Python, a function can take one or more arguments. Arguments are the values that are passed to a function when it is called. Arguments are specified inside the parentheses when defining the function.

Example:
def add_numbers(x, y):
       return x + y


In the example above, ‘x’ and ‘y’ are the arguments that are passed to the function ‘add_numbers()’. When this function is called with two arguments, it returns the sum of those two arguments.

You can pass any data type as an argument to a function, including strings, integers, floats, lists, dictionaries, and even other functions.

Example:
def greet(name):
       print(f"Hello, {name}!")

greet("Alice")  # prints "Hello, Alice!"
greet("Bob")    # prints "Hello, Bob!"


In this example, the function ‘greet()’ takes one argument, which is a string representing a name. When this function is called with a name as an argument, it prints a greeting message using that name.

In addition to positional arguments, Python functions also support keyword arguments, which are arguments passed to a function by explicitly naming them.

Example:
def multiply_numbers(x, y, z=1):
       return x * y * z

result1 = multiply_numbers(2, 3, 4)   # returns 24result2 = multiply_numbers(x=2, y=3, z=4)       # returns 24result3 = multiply_numbers(y=3, x=2, z=4)       # returns 24result4 = multiply_numbers(2, 3)             # returns 6 (uses default value for z)


In this example, the function ‘multiply_numbers()’ takes three arguments, ‘x’, ‘y’, and ‘z’. The z argument has a default value of ‘1’, so it is optional. The function can be called with positional arguments or with keyword arguments. Note that the order of keyword arguments does not matter, but the order of positional arguments does matter.

Functions are a fundamental concept in programming and are widely used in Python. A function is a block of organized and reusable code that performs a specific task. Functions help to keep your code organized, modular, and reusable.

Syntax
In Python, a function is defined using the ‘def’ keyword, followed by the function name and parentheses, with any arguments listed inside the parentheses. The function block starts with a colon and is indented. The ‘return’ statement is used to return a value from the function.

Example:
def function_name(argument1, argument2, ...):
"""docstring - optional"""
statements
...
return value


Once you have defined a function, you can call it from your code by using its name and passing in the necessary arguments, like this:

result = function_name(argument1, argument2, ...)

The function will execute its code and return a value to the caller, which you can store in a variable, print to the console, or use in any other way that you need.

Function Name
The name of a function should be descriptive and give a clear idea of what the function does. Function names should follow the same naming conventions as variables (snake_case). Function names should not be the same as Python built-in functions or reserved keywords.

It is important to choose a descriptive and meaningful name for a function to make the code more readable and understandable. A function name should give a clear idea of what the function does, and should follow the same naming conventions as variables (snake_case).

For example, if you are writing a function to calculate the area of a rectangle, a good function name might be ‘calculate_rectangle_area()’. This name clearly indicates what the function does and follows the naming conventions.

In addition, it is important to avoid using Python built-in functions or reserved keywords as function names. Using these names can cause conflicts and unexpected behavior in your code. For example, you should not name a function ‘print()’ because ‘print()’ is a built-in function in Python.

Docstrings
Docstrings are a way of documenting your code, specifically your functions, in Python. A docstring is a string literal that describes what the function does, how it works, what arguments it takes, and what it returns. The docstring should be placed immediately after the function definition and enclosed in triple quotes.

Example:
def add_numbers(x, y):
"""
This function adds two numbers.

Args:
x (int): First number
y (int): Second number

Returns:
int: Sum of x and y
"""
return x + y


In the example above, the docstring provides a clear explanation of what the function does, what arguments it takes, and what it returns. It also includes type hints for the arguments and return value.

Benefits of Docstrings

Docstrings provide a clear and concise explanation of what a function does, making it easier for other programmers to understand and use your code.

Docstrings can be accessed by the ‘help()’ function or by using the ‘_doc_’ attribute of the function object. This allows other programmers to quickly get information about your function without having to read the code.

Docstrings can be used by automated documentation tools to generate documentation for your code.

Types of Docstrings
There are several conventions for writing docstrings in Python, including:











Best Practices for Docstrings

Here are some best practices for writing effective docstrings:Use clear and concise language to describe what the function does, what arguments it takes, and what it returns.







Return Statements
The return statement is used to exit a function and return a value. When the return statement is executed, the function exits and any code after the return statement is not executed. The value that is returned can be used in other parts of the code.

Example:
def add_numbers(x, y):
       return x + y


In the example above, the ‘return’ statement returns the sum of ‘x’ and ‘y’. The value that is returned can be assigned to a variable or used in an expression:

result = add_numbers(3, 4)
print(result) # prints 7

print(add_numbers(5, 6)) # prints 11

print(add_numbers(1, add_numbers(2, 3))) # prints 6


In the first example, the result of ‘add_numbers(3, 4)’ is assigned to the variable ‘result’, which is then printed. In the second example, the value returned by ‘add_numbers(5, 6)’ is printed directly. In the third example, the value returned by ‘add_numbers(2, 3)’ is added to ‘1’, and the result is passed as an argument to ‘add_numbers()’, which returns the sum of ‘1’ and ‘5’, resulting in a final value of ‘6’.

It's important to note that a function can have multiple ‘return’ statements. When a ‘return’ statement is executed, the function exits immediately and any code after the ‘return’ statement is not executed. This means that a function can have different return values depending on the conditions in the function.

Example:
def divide(x, y):
       if y == 0:
              return None # return None for invalid input
       else:
              return x / y


In the example above, the ‘divide()’ function returns ‘None’ if the ‘y’ argument is ‘0’ to indicate invalid input. If ‘y’ is not ‘0’, the function returns the result of dividing ‘’x by ‘y’.

Function Call
To call a function, you simply write the function name followed by parentheses and any arguments inside the parentheses. The arguments are the values that are passed to the function when it is called.

Example:
def add_numbers(x, y):
       return x + y


result = add_numbers(3, 4)
print(result) # prints 7


In the example above, the ‘add_numbers()’ function is called with arguments ‘3’ and ‘4’. The function returns the sum of ‘x’ and ‘y’, which is then assigned to the variable ‘result’. Finally, the value of the result is printed to the console.

You can also call a function without assigning the result to a variable:

def print_message(message):
       print(message)


print_message("Hello, World!") # prints "Hello, World!"

In this example, the ‘print_message()’ function is called with the argument ‘"Hello, World!"’. The function prints the message to the console. Note that since the function does not return a value, there is no need to assign the result to a variable.

Scope
Scope refers to the accessibility of a variable. In Python, a variable can have either local scope or global scope.

Local Scope: Variables that are defined inside a function have local scope. They can only be accessed within the function. Once the function returns, the variables are destroyed.

Example:
def my_func():
       x = 10 # x has local scope
       print(x)

my_func() # prints 10
print(x) # NameError: name 'x' is not defined


In the example above, ‘x’ is defined inside the ‘my_func()’ function and has local scope. It can only be accessed within the function. When the function returns, ‘x’ is destroyed.

Global Scope: Variables that are defined outside of any function have global scope. They can be accessed anywhere in the program, including inside functions.

Example:
x = 10 # x has global scope

def my_func():
       print(x)

my_func() # prints 10
print(x) # prints 10


In the example above, ‘x’ is defined outside of any function and has global scope. It can be accessed anywhere in the program, including inside the ‘my_func()’ function.

Nonlocal Scope: In nested functions, a variable can have nonlocal scope. This means that it can be accessed by the inner function and the outer function.

Example:
def outer():
       x = 10 # x has outer scope
       def inner():
              nonlocal x # declare x as nonlocal
              x += 1
                     print(x)inner()
       outer() # prints 11


In the example above, ‘x’ is defined in the outer function and has nonlocal scope in the inner function. The ‘nonlocal’ keyword is used to declare ‘x’ as nonlocal, which allows the inner function to access and modify ‘x’. When ‘outer()’ is called, it calls the ‘inner()’ function, which modifies ‘x’ and prints the new value.

It is important to be aware of variable scope when writing code. Using the same variable name for both global and local variables can lead to unexpected results. It is also important to avoid using global variables unless necessary, as they can make code harder to read and maintain.

Default Arguments
You can specify default values for arguments when defining a function. If an argument is not passed to the function, it will use the default value.

Example:
def add_numbers(x, y=1):
       return x + y

print(add_numbers(3)) # prints 4
print(add_numbers(3, 4)) # prints 7


In the example above, the ‘add_numbers()’ function has a default value of ‘1’ for the ‘y’ argument. If the ‘y’ argument is not passed to the function, it will use the default value of 1.

When calling the add_numbers() function with only one argument (add_numbers(3)), the function uses the default value of 1 for the y argument and returns the sum of 3 and 1.

When calling the add_numbers() function with two arguments (add_numbers(3, 4)), the function uses the value 4 for the y argument and returns the sum of 3 and 4.

You can also have multiple default arguments in a function:

def greet(name, greeting="Hello", punctuation="!"):
       return f"{greeting}, {name}{punctuation}"


print(greet("Alice"))                       # prints "Hello, Alice!"
print(greet("Bob", "Hi"))                # prints "Hi, Bob!"
print(greet("Charlie", "Hey", "?")) # prints "Hey, Charlie?"

In the example above, the ‘greet()’ function has three arguments, with default values of "Hello" and "!" for the greeting and punctuation arguments, respectively. When calling the function, you can provide values for any or all of the arguments, and any missing arguments will use their default values.

Sources:

Python documentation: https://docs.python.org/3/tutorial/controlflow.html#defining-functions

W3Schools Python tutorial: https://www.w3schools.com/python/python_functions.asp

Real Python tutorial: https://realpython.com/defining-your-own-python-function/

GeeksforGeeks tutorial: https://www.geeksforgeeks.org/functions-in-python/

Programiz tutorial: https://www.programiz.com/python-programming/function