Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add TP3 #7

Merged
merged 7 commits into from Mar 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
122 changes: 121 additions & 1 deletion TP3/README.md
@@ -1,3 +1,123 @@
# TP3 - Orienté Objet

TODO
## Exercices 1

Utiliser [person.py](./person.py).

#### 1.1 Ajouter des attributs

Rajouter les attributs `email` et `phone` à la classe `Person` et ajouter ce code à `main`:
```py
print(f"Email: {john.email}")
print(f"Phone: {john.phone}")
```

#### 1.2 Method `__str__`

Exécuter `print(john)` dans `main`

```py
def __str__(self) -> str:
return f"{self.first_name} {self.last_name}"
```

Exécuter `print(john)` dans `main` à nouveau.

## Exercice 2

Utiliser [person_2.py](./person_2.py).

Rajouter une ligne au niveau du TODO pour obtenir la sortie suivante :
```
Is John of age: True
Is Bob of age: True
Change the age of majority to 32
Is John of age: False
Is Bob of age: True
```
Cette ligne ne **doit pas utiliser les objets `john` ou `bob`**

## Exercice 3

Utiliser [vectors.py](./vectors.py).

#### 3.1 Création des classes

Un vecteur du plan est composé d'un point de départ et d'un point d'arrivé.
Créez une classe `Point` et une classe `Vector` de manière à représenter des points et des vecteurs du plan en Python.
Vérifier que la fonction `main` s'exécute sans erreurs et que les éléments affichés sont mathématiquements corrects.


#### 3.2 Produit scalaire

Ajouter une méthode qui calcule le produit scalaire de deux vecteurs.


#### 3.3 Ajouter des méthodes spéciales

Ajouter la méthode spéciale `__str__` aux deux classes. Résultat attendu :
```sh
print(point_A) # "Point(-2, -1)"
print(vector_AB) # "Vector(5, 8)"
```

Ensuite, ajouter les méthodes séciales à la classe `Vector`:
- `__add__`
- `__sub__`
- `__neg__`
- `__mul__`
Vérifier que la fonction `main_2` s'exécute sans erreurs et vérifier que les résultats sont mathématiquement corrects.

Pour les annotations de types, on pourra utiliser le type `Self`
```py
from typing import Self
```

#### 3.4 Constructeur alternatif

On souhaite pouvoir créer un vecteur en donnant à l'initialisation seulement le point d'arrivé. Dans ce cas, le point de départ sera l'origine.
On souhaite que ce code fonctionne :
```py
vector_OB = Vector.from_origin(point_B)
```

En utilisant une **méthode de class**, implémenter la méthode `from_origin` sur la classe `Vector`.


## Exercice 4

Utiliser [threads.py](./threads.py).

#### 4.1 Exécuter et comprendre le code

Jusqu'à présent, les programmes que nous avons écrit s'exécutaient dans un seul thread (le thread principal). Exécuter du code dans un nouveau thread permet d'exécuter ce code **en parallèle**.
Il y a plusieurs manières de créer un thread en Python. Pour ce TP, nous allons nous créer une classe qui **hérite de `threading.Thread`**. `threading.Thread` contient plusieurs méthodes et attributs utiles :
- `my_thread.is_alive()`: renvoie True si le thread est en cours d'exécution, False sinon
- `my_thread.start()`: crée le thread et commence l'exécution du code de la méthode `run`
- `my_thread.run()`: code exécuté dans le thread.
- `my_thread.run()`: code exécuté dans le thread.
- `my_thread.join()`: block l'exécution (attend) jusqu'á ce que `my_thread` ait fini (jusqu'à que ce `my_thread.run` ait fini).

A noter qu'un thread ne peut être démarré qu'une seule fois !

Lisez le code, exécutez-le et comprenez son fonctionnement. Vous pouvez changer les valeurs des `time.sleep` et observer le résultat.

Resources pour aller plus loin après le TP :
- https://realpython.com/intro-to-python-threading/

#### 4.2 Factorisation

Le code actuel contient plusieurs problèmes :
- "magic values" pour 1s et 3s utilisés dans time.sleep(...)
- "magic values" pour 1 et 3 utilisés dans for i in range(...)
- duplications de code : les deux classes sont quasi identiques

Factorisez ces deux classes en une seule pour enlever la duplication de code. Pour éviter d'hardcoder les valeurs cités plus haut, passez-les en paramètre de l'initialiseur.
Exemple de code main:
```py
counter1_thread = Counter(stop_value=10, sleep_delay_s=1) # count from 0 to 10 with 1s delay
counter2_thread = Counter(50, 3) # count from 0 to 50 with 3s delay
counter3_thread = Counter(20) # count from 0 to 20 with 1s delay (1s is the default)
```

Vous devrez pour cela *overrider* la méthode `__init__`. Pensez à appeler `super()__init__()`.
19 changes: 19 additions & 0 deletions TP3/person.py
@@ -0,0 +1,19 @@
"""Representation of a person."""


class Person:
def __init__(self, first_name: str, last_name: str, age: int) -> None:
self.first_name = first_name.capitalize()
self.last_name = last_name.upper()
self.age = age


def main():
john = Person("John", "Doe", 30)
print(f"First name: {john.first_name}")
print(f"Last name: {john.last_name}")
print(f"Age: {john.age}")


if __name__ == "__main__":
main()
33 changes: 33 additions & 0 deletions TP3/person_2.py
@@ -0,0 +1,33 @@
"""Representation of a person."""


class Person:

AGE_MAJORITY = 18

def __init__(self, first_name: str, last_name: str, age: int) -> None:
self.first_name = first_name.capitalize()
self.last_name = last_name.upper()
self.age = age

def is_of_age(self) -> bool:
"""Return if the person is over the age of majority or not."""
return self.age >= self.AGE_MAJORITY


def main():
john = Person("John", "Doe", 30)
bob = Person("Bob", "Smith", 34)

print(f"Is John of age: {john.is_of_age()}")
print(f"Is Bob of age: {bob.is_of_age()}")

print("Change the age of majority to 32")
# TODO

print(f"Is John of age: {john.is_of_age()}")
print(f"Is Bob of age: {bob.is_of_age()}")


if __name__ == "__main__":
main()
54 changes: 54 additions & 0 deletions TP3/threads.py
@@ -0,0 +1,54 @@
"""Training with threads."""

import time
from threading import Thread


class Counter1(Thread):
"""Thread counting with 1s delay."""

def start(self) -> None:
super().start()
print("Start counting: delay = 1s")

def run(self) -> None:
for i in range(10):
print(f"Counter 1: {i}")
time.sleep(1)


class Counter3(Thread):
"""Thread counting with 3s delay."""

def start(self) -> None:
super().start()
print("Start counting: delay = 3s")

def run(self) -> None:
for i in range(3):
print(f"Counter 3: {i}")
time.sleep(3)


def main():
counter1_thread = Counter1()
counter3_thread = Counter3()
print(f"{counter1_thread.is_alive() = }")

# Start both threads: both code will run in parallel
counter1_thread.start()
counter3_thread.start()
print(f"{counter1_thread.is_alive() = }")

print("Main thread: sleeping")
time.sleep(12)

# Make sure each thread has finished, or wait until they are finished
print("Main thread: waiting for all threads")
counter1_thread.join()
counter3_thread.join()
print(f"{counter1_thread.is_alive() = }")


if __name__ == "__main__":
main()
49 changes: 49 additions & 0 deletions TP3/vectors.py
@@ -0,0 +1,49 @@
"""Manipulation of 2D-vectors."""

from __future__ import annotations


def main():
origin = Point(0, 0)
point_A = Point(-2, -1)
point_B = Point(3, 7)

print(f"Point O: x={origin.x} y={origin.y}")
print(f"Point A: x={point_A.x} y={point_A.y}")

vector_OA = Vector(origin, point_A)
vector_AB = Vector(point_A, point_B)

print(f"Vector OA: dx={vector_OA.x} dy={vector_OA.y}")
print(f"Vector AB: dx={vector_AB.x} dy={vector_AB.y}")


def main_2():
v = Vector(1, 2)
v2 = Vector(1, 0)
print(v.dot_prod(v2))
print(v2.dot_prod(v))


def main_3():
v = Vector(1, 2)
v2 = Vector(1, 0)
print(-v)
print(-(-v))
print(v + v2)
print(v - v2)
print(v * 3)
print(v2 * -10)


def main_4():
point_B = Point(3, 7)
vector_OB = Vector.from_origin(point_B)
print(vector_OB)


if __name__ == "__main__":
main()
# main_2()
# main_3()
# main_4()