<jupyter_start><jupyter_text>Functions   Definition We use `def` to define a function, and `return` to pass back a value:<jupyter_code>def double(x):
    return x * 2


print(double(5), double([5]), double("five"))<jupyter_output>10 [5, 5] fivefive<jupyter_text>Default Parameters We can specify default values for parameters:<jupyter_code>def jeeves(name="Sir"):
    return "Very good, {}".format(name)
jeeves()
jeeves("James")<jupyter_output><empty_output><jupyter_text>If you have some parameters with defaults, and some without, those with defaults **must** go later. If you have multiple default arguments, you can specify neither, one or both:<jupyter_code>def jeeves(greeting="Very good", name="Sir"):
    return "{}, {}".format(greeting, name)
jeeves()
jeeves("Hello")
jeeves(name="James")
jeeves(greeting="Suits you")
jeeves("Hello", "Sailor")<jupyter_output><empty_output><jupyter_text>Side effects Functions can do things to change their **mutable** arguments,so `return` is optional.This is pretty awful style, in general, functions should normally be side-effect free.Here is a contrived example of a function that makes plausible use of a side-effect<jupyter_code>def double_inplace(vec):
    vec[:] = [element * 2 for element in vec]


z = list(range(4))
double_inplace(z)
print(z)
letters = ["a", "b", "c", "d", "e", "f", "g"]
letters[:] = []<jupyter_output><empty_output><jupyter_text>In this example, we're using `[:]` to access into the same list, and write it's data.    vec = [element*2 for element in vec]would just move a local label, not change the input. But I'd usually just write this as a function which **returned** the output:<jupyter_code>def double(vec):
    return [element * 2 for element in vec]<jupyter_output><empty_output><jupyter_text>Let's remind ourselves of the behaviour for modifying lists in-place using `[:]` with a simple array:<jupyter_code>x = 5
x = 7
x = ["a", "b", "c"]
y = x
x
x[:] = ["Hooray!", "Yippee"]
y<jupyter_output><empty_output><jupyter_text>Early Return Return without arguments can be used to exit early from a function Here's a slightly more plausibly useful function-with-side-effects to extend a list with a specified padding datum.<jupyter_code>def extend(to, vec, pad):
    if len(vec) >= to:
        return  # Exit early, list is already long enough.

    vec[:] = vec + [pad] * (to - len(vec))
x = list(range(3))
extend(6, x, "a")
print(x)
z = list(range(9))
extend(6, z, "a")
print(z)<jupyter_output>[0, 1, 2, 3, 4, 5, 6, 7, 8]<jupyter_text>Unpacking arguments<jupyter_code>def arrow(before, after):
    return str(before) + " -> " + str(after)


arrow(1, 3)<jupyter_output><empty_output><jupyter_text>If a function that takes multiple arguments is given an iterable object prepended with '*',each element of that object is taken in turn and used to fill the function's arguments one-by-one.<jupyter_code>x = [1, -1]
arrow(*x)
# this is my doing and also in the sequence arguments section below
def unpackable(*x):
    return sum(x)

unpackable(1,2,3,4,5,6,7)<jupyter_output><empty_output><jupyter_text>This can be quite powerful:<jupyter_code>charges = {"neutron": 0, "proton": 1, "electron": -1}
for particle in charges.items():
    print(arrow(*particle))<jupyter_output>neutron -> 0
proton -> 1
electron -> -1<jupyter_text>Sequence Arguments Similiarly, if a `*` is used in the **definition** of a function, multiplearguments are absorbed into a list **inside** the function:<jupyter_code>def doubler(*sequence):
    return [x * 2 for x in sequence]
doubler(1, 2, 3)
doubler(5, 2, "Wow!")<jupyter_output><empty_output><jupyter_text>Keyword Arguments If two asterisks are used, named arguments are supplied inside the function as a dictionary:<jupyter_code>def arrowify(**args):
    for key, value in args.items():
        print(key + " -> " + value)


arrowify(neutron="n", proton="p", electron="e")<jupyter_output>neutron -> n
proton -> p
electron -> e<jupyter_text>These different approaches can be mixed:<jupyter_code>def somefunc(a, b, *args, **kwargs):
    print("A:", a)
    print("B:", b)
    print("args:", args)
    print("keyword args", kwargs)
somefunc(1, 2, 3, 4, 5, fish="Haddock")<jupyter_output>A: 1
B: 2
args: (3, 4, 5)
keyword args {'fish': 'Haddock'}
{'fraction_non_alphanumeric': 0.14046665095451333, 'fraction_numerical': 0.013905255715295781, 'mean_word_length': 5.610591900311526, 'pattern_counts': {'":': 3, '<': 48, '<?xml version=': 0, '>': 57, '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': '13', 'dir': 'jupyter-structured-clean-dedup', 'id': '115006', 'license': 'CC-BY-3.0', 'n_tokens_mistral': 1509, 'n_tokens_neox': 1440, 'n_words': 469, 'path': 'module02_intermediate_python/02_01_functions.ipynb', 'repo_name': 'marquesafonso/rse-course', 'size': '4243'}