Functions ========= Function videos --------------- - `Python Tutorial for Beginners 8: Functions `_ - Define a function - ``pass`` - How to execute a function - Benefits of using functions - Return values - Defining parameters - Default parameters - **Advanced-level topics beyond** ``10:30`` (optional viewing) - ``*args`` and ``**kwargs`` - `Python Tutorial: Variable Scope `_ (watch until ``9:20``) - Local variables - Global vairables - `Python Tutorial for Beginners 9: Import Modules and Exploring The Standard Library `_ (watch until ``6:30``) - `Python Tutorial: if __name__ == '__main__' `_ (Optional) - Note: Corey is using Python 2 (old) for this video. Just use the ``print`` function like you are used to. Anatomy of a Function --------------------- .. image:: images/functions_anatomy.png Calling a function ------------------ Up to this point, you are familiar with coding a single program. In reality, a program is made up of smaller sub-programs. We call those sub-programs **functions**. If you look closely at the following code, you will notice you have been using functions the whole time: .. code-block:: python :linenos: name = input("Please enter your name: ") message = f"Hello, {name}. Nice to meet you." print(message) On line #1, we are *calling* the ``input`` function, and on line #2, we are *calling* the ``print`` function. Any time you see some name with parentheses following it ``some_name()``, it is most likely a function. .. figure:: images/functions_coffee.png :width: 500 :align: center Calling a function is like having someone get something for you. Hopefully they bring back what you were asking for. The ``input`` and ``print`` functions were written by someone else and Python automatically includes them as built-in functions. We can also define our own functions that we can use. In fact, when working as a software developer, the majority of the code you write will be in functions. We will see how to :ref:`define custom functions ` a little later. .. figure:: images/function_not_running.png :width: 500 :align: center A function will not run unless you call it. Passing arguments ----------------- Most functions require some information so they can do their job. Take a look at the function below. .. code-block:: python :linenos: def add(a: int, b: int) -> int: return a + b In the function definition on line #1, in the brackets, the defined function tells us it needs two integers to do its job. Therefore, when we :ref:`call the funciton ` we need to give it those **arguments**. In this case, two integers. .. code-block:: python add(5, 7) Each individual **argument** is separated by a comma. Arguments can be of any data-type. The function definition tells us exactly how many arguments and what datatypes. .. figure:: images/functions_passing_arguments.png :width: 500 :align: center Calling ``add(5, 7)`` will place a ``5`` in box 'a' and a ``7`` into box 'b'. Here is another function definition with different datatypes. .. code-block:: python def repeat_message(message: str, times: int) -> str: pass It defines two required arguments: a message of type ``str`` and the number of times (of type ``int``) to repeat the message. To call ``repeat_message`` we need to supply a ``str`` and an ``int`` as **arguments**. .. code-block:: python repeat_message("hello", 5) .. figure:: images/functions_missing_argument.png :width: 500 :align: center If you don't pass the required arguments, Python gets upset. Handling return values ---------------------- Most of the time when writing and using functions, you want the function to go off, do some work for you and return with some result. Imagine you have a friend (some function) that will do your homework for you. You give it the specific questions you need to complete (:ref:`arguments `) and then wait for them to finish it. Now imagine, they look at the questions and just *yelled out* the answers wherever they were. Aside from that being hilarious, it would be useless to you since you would not have anything to turn in to your teacher. This is like using ``print`` in your functions. A lot of beginner programmers love to use ``print`` for everything. Sometimes you can use ``print`` in functions, but moving forward, I would like to formally discourage using print in functions (outside of debugging or showing a user interface like printing menu options). When your friend (the function) completes your homework, it is critical that they give it back to you so you can accomplish the next step of turning it in. If this situation were code, it could potentially look like. .. code-block:: python :linenos: homework_subject = "math" homework_chapter = "chapter 1" completed_work = get_friend_to_do_your_homework(homework_subject, homework_chapter) hand_in(completed_work) Line #3: call the function ``get_friend_to_do_your_homework`` and give it the subject and chapter **arguments**. The function will go off and do its job, then return the completed homework. Your program will then store that completed homework in a variable called ``completed_work``. Line #4: Calls the function ``hand_in`` and gives it the ``completed_work``. In a more practical example, line 9 below calls a function to calculate the tax and stores the result in a variable called ``tax``. .. code-block:: python :linenos: :emphasize-lines: 9 def calc_tax(amount: float) -> float: TAX_RATE = 0.13 return amount * TAX_RATE cost = 6.99 quantity = 3 subtotal = cost * quantity tax = calc_tax(subtotal) total = subtotal + tax Most of the time the pattern will be like that. .. code-block:: python def some_function(): return "some return value" result = some_function() Call the function, get the result and decide what to do with the result *outside* of the function. .. note:: **Why not print things from within the functions themselves?** One person using the function might want to print out the result right away. Someone else might want to do some further processing on the result before printing it out. We want to have our functions do only the bare minimum (i.e., just calculate a value rather than *also* printing it out) to prevent any undesired side-effects. At best, other developers will not want to use our functions, at worse they will curse us because they will have to modify/rewrite them. .. figure:: images/functions_handling_return_value.png :width: 500 :align: center If you call a function that returns a value, you should receive it and store it in a variable. Defining a Function ------------------- Defining a function is much like :ref:`initializing a variable `. Instead of storing a number or a string like in a variable, in a function we store a whole bunch of code. What we need (at a minimum): - a function name - some code to save in the function. .. code:: python def function_name(): print("Some code saved in a function") Every line of code that is stored in the function is indented underneath the function header starting with ``def``. Recall that, in order to actually run the function's code, we need to :ref:`call the function `. Returning a value ----------------- When a function gives back some result. See line 2: .. code-block:: python :linenos: def add(a: int, b: int) -> int: return a + b I like to think of functions as a worker or a friend you know, who can get a specific job done for you. Imagine an intelligence agency needs someone to go and search the database for all mentions of a praticular person of interest. **Intelligence Director**: I need the full profiles of everyone matching the last name ``McGee``. I will wait here patiently until you come back with that list. Hurry up! What are they looking for and what information are they providing? They are looking for "profiles" and they are poviding a last name, "McGee" to be exact. In code, this could look like:: full_profiles = get_profiles_with_name("McGee") The intelligence director would be :ref:`functions:calling a function` (``get_profiles_with_name``) and :ref:`functions:passing an argument ` (``"McGee"``). **Low-level Intelligence Agency Data-gatherer**: Yes, ma'am! I will take this ``name`` and get to work! The low-level intelligence agency data-gatherer was trained to return a list of profiles when someone provided a name. The function definition for this data-gatherer would look like:: def get_profiles_with_name(name: str) -> List[Profile]: # some code # processing.... # still processing.... return list_of_profiles Notice the last thing the data-gatherer does is return the result they were trained to do. **Low-level Intelligence Agency Data-gatherer**: Here is the folder full of the profiles you requested. **Intelligence Director**: Thank you for these profiles. Now I must sort through them and come up with a candidate for my boss. The flow of information would look like: .. warning:: Under construction Defining parameters ------------------- Some functions require no outside information in order to run. This can be slightly restrictive:: def move_ten_meters(): """The robot moves ten meters""". pass move_ten_meters() What if you wanted the robot to move ``15`` meters? Would you create a ``move_fifteen_meters`` function? More useful functions require some outside information to run. We can define functions with certain **parameters**. In the case below, we are defining a ``move`` function to have a ``meters`` parameter: .. code-block:: python :linenos: def move(meters: float): """The robot will move the provided number of meters. Args: meters: The number of meters you want the robot to move. """ print(f"Moving robot {meters} meters.") # more code actually moving the robot # ... move(15) Notice how the function has access to the value (``15``) that was passed to it. It accesses the value (on line #7) through the **parameter-variable** named ``meters``. Type annotations ---------------- Below is a list of common types to use when annotating Python Functions. Some need to be imported from the Python `typing` library:: from typing import List, Tuple, Dict, Optional some_integer: int some_string: str some_float: float list_of_integers: List[int] tuple_with_an_int_and_a_string: Tuple[int, str] two_dimensional_list_of_integers: List[List[int]] either_an_integer_or_none: Optional[int] = None Docstrings ---------- A `docstring `_ is a multi-line comment to explain *in a very concise manner* the purpose of a function and how to use it. To use a funciton you need to know: - its name - what arguments to pass to it - what it will return back to you Have a look at the basic format for docstrings:: def function_with_pep484_type_annotations(param1: int, param2: str) -> bool: """Example function with PEP 484 type annotations. Args: param1: The first parameter. param2: The second parameter. Returns: The return value. True for success, False otherwise. """ pass For docstrings pertaining to Classes, see :ref:`classes:docstrings (classes)`.