We'll start with a brief look at functions. Functions are created using a def statement, which is a statement using the def keyword as its identifying component. As I said earlier, Python executes the statements in a .py file, starting from the top, as shown in the following screenshot:
When Python executes a def statement, it creates a function as a result. This means that the code that runs before the def statement does not see the function because it doesn't exist yet. The part of the def line inside parentheses is called the parameter list:
example_function(name, radius):
The parameter list is a list of internal names for data values that are given to the function as the input. Outside the function, these values might have different names or no names at all, but inside, they'll be stored in these variables.
The indented block of code immediately after the def line is called the function body, and you could think of it as the source code of the function:
def example_function(name, radius):
area = math.pi * radius ** 2
return "The area of {} is {}" .format(name, area)
The following screenshot shows the output of the preceding example:
The code inside the function body is an exception to the rule about running Python code from the top to the bottom of the file. This code is stored away and then executed later, when we tell the function to run.
Like the code in a file, the code in a function runs from top to bottom, one statement or expression at a time.
If you're more familiar with C++ or Java, you may be wondering where the function parameter types and return types are. In Python, the data type is inherent in each data value, so the runtime always knows what type of data we're working with and whether what we're trying to do is a valid operation for that data type. Thus, for the most part, we don't need explicit data types.
Python programmers sometimes talk about duck typing, which is a reference to the following saying:
If it quacks like a duck, it's probably a duck.
What they mean by this saying is that if the operations we're trying to perform on a data value work, it doesn't really matter if it's precisely the kind of data we expected. It's probably close enough. If they don't work, Python will tell us what went wrong and where, which is often more useful to know than the kind of information that can be determined by comparing data types alone.
For situations where we want or need to specify data types, we can use function annotations and the standard library typing module.
Function decorators, which we'll discuss in later chapters, can provide a convenient way of enforcing these annotations.