<jupyter_start><jupyter_text>Functions  Functions and function argumentsFunctions are the building blocks of writing software. If a function is associated with an object and it's data, it is called a method. Functions are defined using the keyword ``def``.There are two types of arguments* regular arguments, which must always be given when calling the function* keyword arguments, that have a default value that can be overriden if desiredValues are returned using the ``return`` keyword. If not ``return`` is defined, the default return value of all functions and methods is **None**, which is the null object in Python.<jupyter_code>def my_function(arg_one, arg_two, optional_1=6, optional_2="seven"):
    return " ".join([str(arg_one), str(arg_two), str(optional_1), str(optional_2)])

print(my_function("a", "b"))
print(my_function("a", "b", optional_2="eight"))

#go ahead and try out different components<jupyter_output><empty_output><jupyter_text>Python has special syntax for catching an arbitary number of parameters. For regular parameters it is a variable with one asterisk \* and for keyword parameters it is a variable with two asterisks. It is conventional to name these ``*args`` and ``**kwargs``, but this is not required.<jupyter_code>def count_args(*args, **kwargs):
    print("i was called with " + str(len(args)) + " arguments and " + str(len(kwargs)) + " keyword arguments")
    
count_args(1, 2, 3, 4, 5, foo=1, bar=2)<jupyter_output><empty_output><jupyter_text>The length of sequences can be checked using the built-in **len()** function. It is standard practice to document a function using **docstrings**. A docstring is just a simple triple-quoted string immediately after the function definition. It is also possible to have docstrings in the beginning of a source code file and after a class definition.<jupyter_code>def random():
    """
    Always the number 4. 
    Chosen by fair dice roll. Guaranteed to be random.
    """
    return 4<jupyter_output><empty_output><jupyter_text>Functions as parametersFunctions are first-class citizens in Python, which means that they can be e.g. passed to other functions. This is the first step into the world of functional programming, an elegant weapon for a more civilized age.<jupyter_code>def print_dashes():
    print("---")
    
def print_asterisks():
    print("***")
    
def pretty_print(string, function):
    function()
    print(string)
    function()
    
pretty_print("hello", print_dashes)
pretty_print("hey", print_asterisks)<jupyter_output>---
hello
---
***
hey
***<jupyter_text>Extra: LambdaWhen we use the keyword `def` we are making a named function. Sometimes we want a simple function to use once without without binding it to any name.Consider the following data structure.<jupyter_code>dictionaries = [
    {"name": "Jack", "age": 35, "telephone": "555-1234"},
    {"name": "Jane", "age": 40, "telephone":  "555-3331"},
    {"name": "Joe", "age": 20, "telephone": "555-8765"}
]<jupyter_output><empty_output><jupyter_text>Now if we want to sort it using Python's built-in `sort()` function the sort won't know which attribute to base the sorting on.Fortunately the `sort()` function takes a named parameter called `key` which is a function to be called on each item in the list. The return value will be used for the name.(Python's `sort()` sorts the list in-place. If you want to keep the list unmodified use `sorted()`)<jupyter_code>def get_age(x):
    return x["age"]

dictionaries.sort(key=get_age)
dictionaries<jupyter_output><empty_output><jupyter_text>This is all nice and well, but now you have a function called get_age that you don't intend to use a second time.An alternative way to give this would be using a lambda expression.<jupyter_code>dictionaries.sort(key=lambda x: x["age"], reverse=True)
dictionaries<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.1032241289651586, 'fraction_numerical': 0.0109204368174727, 'mean_word_length': 5.21486268174475, 'pattern_counts': {'":': 9, '<': 28, '<?xml version=': 0, '>': 28, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '7', 'dir': 'jupyter-structured-clean-dedup', 'id': '475848', 'license': 'MIT', 'n_tokens_mistral': 1136, 'n_tokens_neox': 1079, 'n_words': 464, 'path': 'notebooks/examples/3 - Functions.ipynb', 'repo_name': 'smasoka/python-introduction', 'size': '3846'}