Update S6: Object Oriented Programming authored by Rodrigo's avatar Rodrigo
![](https://github.com/rodperex/PNE-wiki/blob/master/s6-OOP/Cover/Cover.png) ![](https://gitlab.etsit.urjc.es/rperez/pne-wiki/-/raw/master/s6-OOP/Cover/Cover.png)
# Session 6: Object Oriented Programming # Session 6: Object Oriented Programming
* **Time**: 2h * **Time**: 2h
* **Date**: Tuesday, Feb-15th-2022 * **Date**: XXXX, XX-XX-2023
* **Goals**: * **Goals**:
* Understand how the Classes work * Understand how the Classes work
* Create our first Class * Create our first Class
...@@ -16,9 +16,9 @@ ...@@ -16,9 +16,9 @@
* [Working with sequences and functions](#working-with-sequences-and-functions) * [Working with sequences and functions](#working-with-sequences-and-functions)
* [Modelling the sequences with object oriented programming](#modelling-the-sequences-with-object-oriented-programming) * [Modelling the sequences with object oriented programming](#modelling-the-sequences-with-object-oriented-programming)
* [Classes](#classes) * [Classes](#classes)
* [The \_\_Init\_\_ method](#the-__init__-method) * [The \__Init_\_ method](#the-\__init_\_-method)
* [Adding data: Atribute strbases](#adding-data-attribute-strbases) * [Adding data: Atribute strbases](#adding-data-attribute-strbases)
* [The \_\_str\_\_ method](#the-__str__-method) * [The \__str_\_ method](#the-\__str_\_-method)
* [Adding methods: len()](#adding-methods-len) * [Adding methods: len()](#adding-methods-len)
* [Inheritance](#inheritance) * [Inheritance](#inheritance)
* [Expanding the Gene class](#expanding-the-gene-class) * [Expanding the Gene class](#expanding-the-gene-class)
...@@ -62,14 +62,15 @@ print(f" G: {seq_count_base(seq1, 'G')}") ...@@ -62,14 +62,15 @@ print(f" G: {seq_count_base(seq1, 'G')}")
``` ```
The headers of the functions in Seq0 that have changed are (you may want to try to implement it this way): The headers of the functions in Seq0 that have changed are (you may want to try to implement it this way):
```python3 ```python3
def seq_count_base(sequence, base=None): def seq_count_base(sequence, base=None):
# code # code
def seq_len(seq=None) def seq_len(seq=None)
# code # code
``` ```
This is what you get in the **console** when the program is **executed**: This is what you get in the **console** when the program is **executed**:
``` ```
...@@ -102,14 +103,12 @@ print(f" C: {seq_count_base(seq1, 'C')}") ...@@ -102,14 +103,12 @@ print(f" C: {seq_count_base(seq1, 'C')}")
print(f" G: {seq_count_base(seq1, 'G')}") print(f" G: {seq_count_base(seq1, 'G')}")
# -- But this program works normally. No error es detected # -- But this program works normally. No error es detected
``` ```
* How could you solve that problem? How could we guarantee that the introduced sequence is valid? * How could you solve that problem? How could we guarantee that the introduced sequence is valid?
One solution is adding a **new function** for checking that a given sequence is ok. Something like this: One solution is adding a **new function** for checking that a given sequence is ok. Something like this:
```python3 ```python3
... ...
seq1 = ATTXMMNXCCCGGGG" seq1 = ATTXMMNXCCCGGGG"
...@@ -139,7 +138,7 @@ We will learn this model by defining **sequence objects** from **scratch** ...@@ -139,7 +138,7 @@ We will learn this model by defining **sequence objects** from **scratch**
### Classes ### Classes
A [class](https://en.wikipedia.org/wiki/Class_(computer_programming)) is the **template** we use for **creating objects** (an object is going to be an instance of that class). Within the class we define and implement all the **methods** that the objects of that class will have. Let's create a very simple class for working with sequences. We start by defining an **empty class**: A [class](https://en.wikipedia.org/wiki/Class\_(computer_programming)) is the **template** we use for **creating objects** (an object is going to be an instance of that class). Within the class we define and implement all the **methods** that the objects of that class will have. Let's create a very simple class for working with sequences. We start by defining an **empty class**:
```python ```python
class Seq: class Seq:
...@@ -160,15 +159,15 @@ s2 = Seq() ...@@ -160,15 +159,15 @@ s2 = Seq()
Let's place a **breakpoint** in line 7 Let's place a **breakpoint** in line 7
![](https://github.com/rodperex/PNE-wiki/blob/master/s6-OOP/oop-01.png) ![](https://gitlab.etsit.urjc.es/rperez/pne-wiki/-/raw/master/s6-OOP/oop-01.png)
Press the **step over** option twice. On the variable panel we will see the **two new objects** created: **s1** and **s2**. Notice that they are of **type Seq** Press the **step over** option twice. On the variable panel we will see the **two new objects** created: **s1** and **s2**. Notice that they are of **type Seq**
![](https://github.com/rodperex/PNE-wiki/blob/master/s6-OOP/oop-02.png) ![](https://gitlab.etsit.urjc.es/rperez/pne-wiki/-/raw/master/s6-OOP/oop-02.png)
Congrats! You have created your first two **empty objects**! Congrats! You have created your first two **empty objects**!
### The \_\_init\_\_ method ### The \__init_\_ method
The **methods** are the **actions** that the objects can perform. The first method we are implementing is the **initialization method**. It is a **special method** that is called every time a new object is created. All the methods have the **special parameter self** as the first parameter The **methods** are the **actions** that the objects can perform. The first method we are implementing is the **initialization method**. It is a **special method** that is called every time a new object is created. All the methods have the **special parameter self** as the first parameter
...@@ -195,14 +194,13 @@ Testing.... ...@@ -195,14 +194,13 @@ Testing....
``` ```
* Execute it **step by step**, using the **step over** command * Execute it **step by step**, using the **step over** command
* Execute it **step by step** using the **step into** command every time. Check that the debugger **enters** into the **Class** and execute the\*\* \__init_\_\*\* method
* Execute it **step by step** using the **step into** command every time. Check that the debugger **enters** into the **Class** and execute the** \_\_init\_\_** method
### Adding data: attribute strbases ### Adding data: attribute strbases
For representing a **sequence** we will use a **string** that will be stored in every object. We will call this string **strbases**. Data stored in the objects are referred as **attributes** For representing a **sequence** we will use a **string** that will be stored in every object. We will call this string **strbases**. Data stored in the objects are referred as **attributes**
Let's modify the \_\_init\_\_ method to include a new parameter: the string for creating the object. That parameter will be stored in the object attribute: **self.strbases** Let's modify the \__init_\_ method to include a new parameter: the string for creating the object. That parameter will be stored in the object attribute: **self.strbases**
```python ```python
class Seq: class Seq:
...@@ -220,15 +218,16 @@ class Seq: ...@@ -220,15 +218,16 @@ class Seq:
s1 = Seq("AGTACACTGGT") s1 = Seq("AGTACACTGGT")
s2 = Seq("CGTAAC") s2 = Seq("CGTAAC")
``` ```
If you **execute** it, you will see the **same output** as before, but now something has happened. The two objects created have their own **sequence string** stored in the **strbases** attribute. Let's debug it to see it If you **execute** it, you will see the **same output** as before, but now something has happened. The two objects created have their own **sequence string** stored in the **strbases** attribute. Let's debug it to see it
![](https://github.com/rodperex/PNE-wiki/blob/master/s6-OOP/oop-03.png) ![](https://gitlab.etsit.urjc.es/rperez/pne-wiki/-/blob/master/s6-OOP/oop-03.png)
Each object has **its own sequence**! If you **debug** it using the **step into** option, you will see that the sequence is stored in the object when the **self.strbases = strbases** line is executed Each object has **its own sequence**! If you **debug** it using the **step into** option, you will see that the sequence is stored in the object when the **self.strbases = strbases** line is executed
### The \_\_str\_\_ method ### The \__str_\_ method
There is another special method, called **\_\_str\_\_** that is invoked whenever the object is **printed**. Printing our objects means that we want to see the **sequence** on the **console** There is another special method, called **\__str_\_** that is invoked whenever the object is **printed**. Printing our objects means that we want to see the **sequence** on the **console**
```python3 ```python3
class Seq: class Seq:
...@@ -258,6 +257,7 @@ print(f"Sequence 1: {s1}") ...@@ -258,6 +257,7 @@ print(f"Sequence 1: {s1}")
print(f"Sequence 2: {s2}") print(f"Sequence 2: {s2}")
print("Testing....") print("Testing....")
``` ```
After **running** it, we will see the following **messages** on the **console**: After **running** it, we will see the following **messages** on the **console**:
``` ```
...@@ -268,7 +268,7 @@ Sequence 2: CGTAAC ...@@ -268,7 +268,7 @@ Sequence 2: CGTAAC
Testing.... Testing....
``` ```
In the **debug mode**, if the **step into** option is pressed when the next instruction to be executed is the first print, you will see how the execution pointer moved into the **\_\_str\_\_ method** In the **debug mode**, if the **step into** option is pressed when the next instruction to be executed is the first print, you will see how the execution pointer moved into the **\__str_\_ method**
### Adding methods: len() ### Adding methods: len()
...@@ -363,7 +363,7 @@ Gene: CGTAAC ...@@ -363,7 +363,7 @@ Gene: CGTAAC
The **Gene** is a kind of **specialized sequence**. Not all the sequences are Genes, but all the genes are sequences. We can **expand** the **Gene class** by adding more information that is not present in the general sequences. For example the **name** (usually the genes have a special name) The **Gene** is a kind of **specialized sequence**. Not all the sequences are Genes, but all the genes are sequences. We can **expand** the **Gene class** by adding more information that is not present in the general sequences. For example the **name** (usually the genes have a special name)
Let's create a new \_\_init\_\_ file method that includes a new optional parameter: the name of the Gene Let's create a new \__init_\_ file method that includes a new optional parameter: the name of the Gene
```python3 ```python3
class Gene(Seq): class Gene(Seq):
...@@ -380,7 +380,7 @@ class Gene(Seq): ...@@ -380,7 +380,7 @@ class Gene(Seq):
print("New gene created") print("New gene created")
``` ```
As the Gene is also a Seq, for creating a Gene first we should call the **init** function from the Seq class. We do it by calling the **super().__init__(strbases)** method, and then we add the properties of the Gene. In this case, only its name As the Gene is also a Seq, for creating a Gene first we should call the **init** function from the Seq class. We do it by calling the **super().init(strbases)** method, and then we add the properties of the Gene. In this case, only its name
When creating the Gene in the main program, we specify its **name** as a parameter, for example FRAT1: When creating the Gene in the main program, we specify its **name** as a parameter, for example FRAT1:
...@@ -408,7 +408,7 @@ Notice how the **init** function from Seq has been **called twice**, and the ini ...@@ -408,7 +408,7 @@ Notice how the **init** function from Seq has been **called twice**, and the ini
#### Overriding the Seq methods #### Overriding the Seq methods
In the new Gene class, we can create **new methods**, or we can **re-implement** methods that were already implemented in the *mother/father* class (Seq in this case). This operation of re-implementation is called **overriding**. For example, let's change the \_\_str\_\_ method for **printing** the **Gene name** along with the sequence In the new Gene class, we can create **new methods**, or we can **re-implement** methods that were already implemented in the _mother/father_ class (Seq in this case). This operation of re-implementation is called **overriding**. For example, let's change the \__str_\_ method for **printing** the **Gene name** along with the sequence
```python3 ```python3
class Gene(Seq): class Gene(Seq):
...@@ -442,6 +442,7 @@ print(f"Gene: {g}") ...@@ -442,6 +442,7 @@ print(f"Gene: {g}")
``` ```
This is the result of the **execution**: This is the result of the **execution**:
``` ```
New sequence created! New sequence created!
New sequence created! New sequence created!
...@@ -449,16 +450,16 @@ New gene created ...@@ -449,16 +450,16 @@ New gene created
Sequence 1: AGTACACTGGT Sequence 1: AGTACACTGGT
Gene: FRAT1-CGTAAC Gene: FRAT1-CGTAAC
``` ```
If you have a look to the right side of the \_\_str\_\_ method in the **Gene Class**, you will notice a new icon: a **circle with an arrow** pointing upwards. This means that this methods is **overriding** another implemented in the super class (Seq)
![](https://github.com/rodperex/PNE-wiki/blob/master/s6-OOP/oop-04.png) If you have a look to the right side of the \__str_\_ method in the **Gene Class**, you will notice a new icon: a **circle with an arrow** pointing upwards. This means that this methods is **overriding** another implemented in the super class (Seq)
On the other hand, if you have a look at the \_\_str\_\_ method in the **Seq Class**, you will see the previous icon and a **new one** with another circle and **arrow pointing downwards** ![](https://gitlab.etsit.urjc.es/rperez/pne-wiki/-/blob/master/s6-OOP/oop-04.png)
![](https://github.com/rodperex/PNE-wiki/blob/master/s6-OOP/oop-05.png) On the other hand, if you have a look at the \__str_\_ method in the **Seq Class**, you will see the previous icon and a **new one** with another circle and **arrow pointing downwards**
This means that there is a **sub-class** that is overriding this method. And that the Seq \_\_str\_\_ method is also overriding another one from its **super class** (generic) ![](https://gitlab.etsit.urjc.es/rperez/pne-wiki/-/blob/master/s6-OOP/oop-05.png)
This means that there is a **sub-class** that is overriding this method. And that the Seq \__str_\_ method is also overriding another one from its **super class** (generic)
## Installing libraries: termcolor ## Installing libraries: termcolor
...@@ -474,17 +475,17 @@ termcolor.cprint("Hey! this is printed in green!", 'green') ...@@ -474,17 +475,17 @@ termcolor.cprint("Hey! this is printed in green!", 'green')
You will see that the word **termcolor** is underlined in **red**. This is because Pycharm does not know anything about the termcolor module. You will see that the word **termcolor** is underlined in **red**. This is because Pycharm does not know anything about the termcolor module.
![](https://github.com/rodperex/PNE-wiki/blob/master/s6-OOP/libraries-02.png) ![](https://gitlab.etsit.urjc.es/rperez/pne-wiki/-/blob/master/s6-OOP/libraries-02.png)
Place the **mouse pointer** on termcolor and a **new window** will pop up: Place the **mouse pointer** on termcolor and a **new window** will pop up:
![](https://github.com/rodperex/PNE-wiki/blob/master/s6-OOP/libraries-01.png) ![](https://gitlab.etsit.urjc.es/rperez/pne-wiki/-/blob/master/s6-OOP/libraries-01.png)
Click on the **install package termcolor** option. The package start the installation. After some seconds it will be ready so the termcolor word will be no longer in red Click on the **install package termcolor** option. The package start the installation. After some seconds it will be ready so the termcolor word will be no longer in red
Now **run** the program. You will see a green message: Now **run** the program. You will see a green message:
![](https://github.com/rodperex/PNE-wiki/blob/master/s6-OOP/libraries-03.png) ![](https://gitlab.etsit.urjc.es/rperez/pne-wiki/-/blob/master/s6-OOP/libraries-03.png)
You now have the power of printing in colors...use it wisely :) You now have the power of printing in colors...use it wisely :)
...@@ -496,7 +497,7 @@ Let's practice with the objects!. Create the **Session-06 folder** if you alread ...@@ -496,7 +497,7 @@ Let's practice with the objects!. Create the **Session-06 folder** if you alread
### Exercise 1 ### Exercise 1
The current **Seq class** created as a example in this session does not check if the given string of bases is **valid*. Therefore, if we execute the following code in the the main program: The current **Seq class** created as a example in this session does not check if the given string of bases is \*_valid_. Therefore, if we execute the following code in the the main program:
```python3 ```python3
s1 = Seq("ACCTGC") s1 = Seq("ACCTGC")
...@@ -515,7 +516,7 @@ New sequence created! ...@@ -515,7 +516,7 @@ New sequence created!
The goal of this exercise is to detect the **incorrect sequences** The goal of this exercise is to detect the **incorrect sequences**
* **Filename**: S6/e1.py * **Filename**: S6/e1.py
* **Description**: Modify the \_\_ini\_\_ method of the Seq class so that it detects that the given string only have these four valid bases: 'A', 'C', 'G' and 'T'. If a different character is found, the sequence should be initialized with the **"ERROR"** string, and the message **"INCORRECT Sequence detected"** should be printed in the console * **Description**: Modify the \__ini_\_ method of the Seq class so that it detects that the given string only have these four valid bases: 'A', 'C', 'G' and 'T'. If a different character is found, the sequence should be initialized with the **"ERROR"** string, and the message **"INCORRECT Sequence detected"** should be printed in the console
When the previous **main program** is executed, this is what should be printed on the console: When the previous **main program** is executed, this is what should be printed on the console:
...@@ -549,7 +550,7 @@ Sequence 2: (Length: 6) CAGATA ...@@ -549,7 +550,7 @@ Sequence 2: (Length: 6) CAGATA
### Exercise 3 ### Exercise 3
We need to develop some **functions** to create sequences for **testing** the Seq objects. For instance, the function **generate_seqs(pattern, number)**, that has two parameters, will create a **list** with the provided *number* of sequences. All the sequences are created from the given **pattern**. This pattern is a string of one or more bases. The first sequence of the list will have the provided pattern, the second, the pattern will be repeated twice, in the third the patter will be repeated three times, and so on We need to develop some **functions** to create sequences for **testing** the Seq objects. For instance, the function **generate_seqs(pattern, number)**, that has two parameters, will create a **list** with the provided _number_ of sequences. All the sequences are created from the given **pattern**. This pattern is a string of one or more bases. The first sequence of the list will have the provided pattern, the second, the pattern will be repeated twice, in the third the patter will be repeated three times, and so on
Therefore, if we call the function generate_seqs() with the parameters ("A", 3), a list of 3 sequences is returned. The bases in every sequence will be: "A", "AA" and "AAA" Therefore, if we call the function generate_seqs() with the parameters ("A", 3), a list of 3 sequences is returned. The bases in every sequence will be: "A", "AA" and "AAA"
...@@ -601,8 +602,7 @@ You should modify the **print_seqs()** function for including an additional para ...@@ -601,8 +602,7 @@ You should modify the **print_seqs()** function for including an additional para
* **Filename**: S6/e4.py * **Filename**: S6/e4.py
* **Description**: The same output than e3.py, but in colors. This is how it should looks like: * **Description**: The same output than e3.py, but in colors. This is how it should looks like:
![](https://github.com/rodperex/PNE-wiki/blob/master/s6-OOP/ex4-01.png) ![](https://gitlab.etsit.urjc.es/rperez/pne-wiki/-/blob/master/s6-OOP/ex4-01.png)
## END of the session ## END of the session
...@@ -622,11 +622,10 @@ The session is finished. Make sure, during this week, that everything in this li ...@@ -622,11 +622,10 @@ The session is finished. Make sure, during this week, that everything in this li
# Credits # Credits
* Rodrigo Pérez Rodríguez * Rodrigo Pérez Rodríguez
* Álvaro del Castillo San Félix
# License # License
![](https://github.com/rodperex/PNE-wiki/blob/c46277dc03421f86d55cb3b1454e3624ac7075b1/Cover/attribution-share-alike-creative-commons-license.png) ![](https://gitlab.etsit.urjc.es/rperez/PNE/-/raw/master/Cover/attribution-share-alike-creative-commons-license.png)
# Links # Links
... ...
......