<jupyter_start><jupyter_text>Methods and functionsIn the introduction notebook we have learned about the concept of function which is similar to a mathematical function such as $y = f(x, a)$ with $f(x, a) = a * x^2$. Here we have inputs $x, a$, an output $y$ and a function definition $f(x, a) = a * x^2$. To recap such as function is written in the following way in Python:<jupyter_code>def f(x, a):
    y = a * x**2
    return y<jupyter_output><empty_output><jupyter_text>And used naturally as:<jupyter_code>out = f(3, 4)
out<jupyter_output><empty_output><jupyter_text>There are different types of functions and inputs in Python and now we do a quick tour of them.  Built-in functionsWe have already seen that if we define a string such as:<jupyter_code>mystring = 'this is my string'<jupyter_output><empty_output><jupyter_text>we can get the number of characters using a function called ```len()```:<jupyter_code>len(mystring)<jupyter_output><empty_output><jupyter_text>```len()``` is a a built-in function of Python. It is accessible without any import statement and can be used in different contexts. For example if we have a list, we can also ask its length:<jupyter_code>mylist = [1,6,3,9]
len(mylist)<jupyter_output><empty_output><jupyter_text>There is a series of such functions in Python. In this course, we will mostly be using ```len```, as well as ```type()``` and ```print()```. The ```type()``` function tells us what kind of variable we are dealing with. For example we can ask what's the type of ```mystring```:<jupyter_code>type(mystring)<jupyter_output><empty_output><jupyter_text>We see that we are dealing here with a string i.e. a text variable. If we define a simple number:<jupyter_code>b = 3
type(b)<jupyter_output><empty_output><jupyter_text>we see that we are dealing with an integer. Conversely if we define a number with a comma:<jupyter_code>c = 3.4
type(c)<jupyter_output><empty_output><jupyter_text>Python tells us that we are dealing with a floating point number. As you can see, contrary to other languages, in Python we don't explicitly define the type of variables, it's rather Python that infers the type from our input.The ```print()``` function allows us to explicitly display a variable and is mostly used when dealing with longer chunks of code. For example:<jupyter_code>print(mystring)<jupyter_output>this is my string<jupyter_text>Methods In addition to functions, all variables in Python have functions that are specifically **attached** to them, in which case they are called **methods**. The functioning of these method-functions is the same as that of regular functions except for the way we call them. As they are attached to a variable, we can call them by using a dot notation.For example any string can be capitalized using the ```capitalize()``` method which is used like this:<jupyter_code>mystring.capitalize()<jupyter_output><empty_output><jupyter_text>So the only difference is that instead of writing ```f(mystring)``` we write ```mystring.f()```.In our ```capitalize``` example, the function takes no input, which is why we only have an empty parenthesis. However sometimes there is an additional input ```x``` in which case we have to write ```mystring.f(x)```. For example the ```find()``` method, which indicates where in the string one finds a specific substring:<jupyter_code>mystring
mystring.find('my')<jupyter_output><empty_output><jupyter_text>For the moment you can consider functions and methods in the same way. The concept of method comes from the object oriented programming that we will not explicitly cover during this course.  Getting infos If you want to know all the functions and methods associated with a variable, you can use the function ```dir()```. For example for our string this gives:<jupyter_code>dir(mystring)<jupyter_output><empty_output><jupyter_text>All the functions are placed between two __ characters, the other are methods. We see that in that list we find again the ```len()``` function as well as the ```capitalize()``` method. Often one is unsure what a function exactly does and what input it needs. In that case one can either Google the answer, or use the built-in help.To access the built-in help, one can just use the ```help()``` function or the ```?```mark. For example if we wanted to know what the ```capitalize()``` functions was doing we would type:<jupyter_code>help(mystring.capitalize)
mystring.capitalize?<jupyter_output><empty_output><jupyter_text>**In Jupyter, you also get infos on a function by placing the cursor within the function parenthesis and typing Shift+Tab.**  Compounding functions When we apply a series of functions we can of course separate all steps:<jupyter_code>myint = -23
abs_int = abs(myint)
float_abs_int = float(abs_int)
print(float_abs_int)<jupyter_output>23.0<jupyter_text>But we can also just nest them into each other:<jupyter_code>float_abs_int = float(abs(myint))
print(float_abs_int)<jupyter_output>23.0<jupyter_text>The same is true for methods which are then chained together:<jupyter_code>mystring = 'this is my string'
mystring2 = mystring.upper()
print(mystring2)
mystring2.find('MY')<jupyter_output><empty_output><jupyter_text>is equivalent to:<jupyter_code>mystring.upper().find('MY')<jupyter_output><empty_output><jupyter_text>How much you separate different steps should be a compromise between compactness of code and readability.  Custom functionsWe have already seen how to create our own functions. We here give some more details on additional options. Above we defined:<jupyter_code>def f(x, a):
    y = a * x**2
    return y<jupyter_output><empty_output><jupyter_text>Let's recapitulate the important parts of this piece of code:- ```def``` says we are defining a function- ```my_function``` is the name of the function, i.e. like $f$ in our mathematical example- ```x``` and ```a``` are our inputs like in $f(x, a)$- ```return```specifies what the output of the function is- this output here is ```y```, the equivalent of $y$ in our mathematical function $y = f(x, a)$You can then use it as any function:<jupyter_code>f(3, 3)<jupyter_output><empty_output><jupyter_text>When we call the function, we can either pass just the inputs and make sure that we have the rigth number of them or we can explicitly write the name of the input variables. In the first case, we need to pass the inputs in the **correct order**, as Python just assumes that we use the same order as defined in the function. In the second case, we don't have to follow the order:<jupyter_code>f(a=4, x=6)<jupyter_output><empty_output><jupyter_text>Often we write functions that can take multiple inputs but some of those inputs *usually* take a given value. To spare us having to repeatedly state this value that rarely changes, we can define **default values** for our function:<jupyter_code>def f(x, a=3):
    y = a * x**2
    return y<jupyter_output><empty_output><jupyter_text>Now when we call the function, if we don't specify an input for ```a```, Python will just assume that its value is 3:<jupyter_code>f(x=2)<jupyter_output><empty_output><jupyter_text>or<jupyter_code>f(2)<jupyter_output><empty_output><jupyter_text>It's generally a good policy to explicitly state the input variables when calling a function, as it avoid confusion. Let's imagine that we have the following new function:<jupyter_code>def f2(x, a=3, b=5):
    y = a * x**2 + b
    return y<jupyter_output><empty_output><jupyter_text>If we call ```f2``` like this:<jupyter_code>f2(3,4)<jupyter_output><empty_output><jupyter_text>It means that in the function we used ```x=3```, ```a=4``` and ```b=5```. It would be clearer in that case to use:<jupyter_code>f2(x=3, a=4)<jupyter_output><empty_output><jupyter_text>or even  External packagesWe have learned in the introduction that sets of specialized functions are imported from external packages. We have seen that we need to first install such packages (just the first time we use them) and then import them in the notebooks. For example the library that implements the computationally efficient lists called **arrays** is Numpy. If we want to import it we write:<jupyter_code>import numpy<jupyter_output><empty_output><jupyter_text>Many of the most common libraries have abbreviations to keep the code shorter. This is the case for Numpy which almost always gets imported as:<jupyter_code>import numpy as np<jupyter_output><empty_output><jupyter_text>Numpy has an extensive set of functions that we can use by calling them using the dot notation. For example there is a function that computes the natural log of a number:<jupyter_code>np.log(4)<jupyter_output><empty_output><jupyter_text>As you can see, we use these functions in the same way as the built-in functions or our custom functions: a function name followed by a parenthesis and the necessary inputs. Here again you can get information about a function using the help:<jupyter_code>np.log?<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.09061416265801543, 'fraction_numerical': 0.005369728157512026, 'mean_word_length': 5.957198443579767, 'pattern_counts': {'":': 0, '<': 118, '<?xml version=': 0, '>': 118, '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': '30', 'dir': 'jupyter-structured-clean-dedup', 'id': '658811', 'license': 'BSD-3-Clause', 'n_tokens_mistral': 2709, 'n_tokens_neox': 2640, 'n_words': 1148, 'path': 'Appendix_Functions.ipynb', 'repo_name': 'guiwitz/Python_image_processing_beginner', 'size': '8939'}