Cours 2 Imbrication de types, classes et objets

Quentin Lurkin

Imbrication de données

Liste à deux dimensions

A = [1, 2]
B = [3, 4, 5]
L = [A, B]                # Équivalent à L = [[1, 2], [3, 4, 5]]

print(L[0][1])            # 2
print(L[1][2])            # 5
Liste 2D

Parcours d’une liste à deux dimensions

Parcours d’une liste à deux dimensions

L = [[1, 2], [3, 4, 5]]

s = 0

for elem in L:            # elem est une liste
  for data in elem:       # data est un nombre entier
    s += data

print(s)
15

Représentation d’une matrice

M = [[1, 2, 3], [4, 5, 6]]

M=(123456) Par exemple, M[1][0] vaut 4 M = \begin{pmatrix} 1 & 2 & 3 \\ 4 & 5 & 6 \end{pmatrix} \text{ Par exemple, M[1][0] vaut 4}

Parcours d’une matrice

# Avec une boucle while
i = 0
while i < len(M):
 j = 0
 while j < len(M[0]):
  print(M[i][j])
  j += 1
 i += 1

# Avec une boucle for
for line in M:
 for elem in line:
  print(elem)

Structures imbriquées

# Liste de tuples
coords = [(0,0), (7,-2), (4,5), (-3,-9)]

# Liste d'ensembles
lunches = [
  {'apple', 'banana', 'grape'},
  {'yogurt', 'cereals'},
  {'bread', 'cheese', 'ham'},
  {'sausage'}
]

# Liste de dictionnaires
contacts = [
  {'firstname': 'Alexis', 'lastname': 'King'},
  {'firstname': 'Brice', 'lastname': 'Monster'},
  {'firstname': 'Sébastien', 'lastname': 'Adams'}
]

Structures imbriquées

# Tuples en clés d'un dictionnaire
config = {
  (0, 0): 'Arnaud',
  (2, 1): 'Louis',
  (-1, 3): 'Marie',
  (3, -1): 'Dan'
}

# Listes en valeurs d'un dictionnaire
config = {
  (0, 0): ['Arnaud', 'Pierre'],
  (2, 1): ['Louis'],
  (-1, 3): ['Marie', 'Éric', 'Tom'],
  (3, -1): ['Dan']
}

Imbrication complexes

address = {'street': "Promenade de l'Alma", 'number': 50,
  'zip': 1200, 'city': "Woluwé-Saint-Lambert"}
marchand = {'firstname': "Cédric", 'lastname': "Marchand",
  'address': address}

# Équivalent à
# marchand = {'firstname': "Cédric", 'lastname': "Marchand",
# 'address': {'street': "Promenade de l'Alma", 'number': 50,
# 'zip': 1200, 'city': "Woluwé-Saint-Lambert"}}

print(marchand['firstname'])          # Cédric
print(marchand['address']['city'])    # Woluwé-Saint-Lambert

Attention aux références

def magic1(data: list[int]):
    data.append(4)

def magic2(data: list[int]):
    data = []

a = [0, 1, 2, 3]
print(a)

magic1(a)
print(a)

magic2(a)
print(a)

Copie

L = [1, 2, 3, 4, 5]
A = L                                   # A est un alias de L
A[0] = 42
print(L)                                # [42, 2, 3, 4, 5]

L = [1, 2, 3, 4, 5]
B = list(L)                             # B est une copie de L
B[0] = 42
print(L)                                # [1, 2, 3, 4, 5]

Copie

l’assignation ne crée pas de copie

Copie de structures imbriquées

L = [[1, 2], [3, 4, 5]]
A = list(L)

A[1][0] = 42
print(L)                 # [[1, 2], [42, 4, 5]]
Shallow copy

Module copy

import copy

L = [[1], [2, 3], [4, 5, 6]]
A = copy.copy(L)                   # A est une copie shallow de L
B = copy.deepcopy(L)               # A est une copie deep de L

Classes et objets

address = {'street': "Promenade de l'Alma", 'number': 50,
  'zip': 1200, 'city': "Woluwé-Saint-Lambert"}
marchand = {'firstname': "Cédric", 'lastname': "Marchand",
  'address': address}

print(type(address))      # <class dict>
print(type(marchand))     # <class dict>

Classes et objects

from typing import Any

def fullname(person: dict[str, Any]) -> str:
  return f"{person['firstname']} {person['lastname']}"

def display_address(address: dict[str, Any]) -> str:
  return f"{address['street']}, {address['number']}\n{address['zip']} {address['city']}"

print(display_address(marchand))  # KeyError

Classes et objets

Création d’une classe

class Address:
  def __init__(self, street: str, number: int, zip: int, city: str):
    self.street = street
    self.number = number
    self.zip = zip
    self.city = city


address = Address("Promenade de l'Alma", 50, 1200, "Woluwé-Saint-Lambert")
print(address.zip)        # 1200

Fonctionnalités d’une classe

class Address:
  def __init__(self, street: str, number: int, zip: int, city: str):
    self.street = street
    self.number = number
    self.zip = zip
    self.city = city

  def display(self):
    return f"{self.street}, {self.number}\n{self.zip} {self.city}"

address = Address("Promenade de l'Alma", 50, 1200, "Woluwé-Saint-Lambert")
print(address.display())        # Promenade de l'Alma, 50
                                # 1200 Woluwé-Saint-Lambert

Objet et classe

Attribut et fonctionnalité

Utilisation d'un objet

# Construction d'un objet
address = Address("Promenade de l'Alma", 50, 1200, "Woluwé-Saint-Lambert")

# Accès à un attribut
print(address.zip)              # 1200

# Appel d'une méthode
print(address.display())        # Promenade de l'Alma, 50
                                # 1200 Woluwé-Saint-Lambert

Définir une classe

class Person:
    pass

Créer une instance

a = Person()
b = Person()
Construction d’objets

Définir un constructeur

class Person:
  def __init__(self, firstname: str, lastname: str, address: Address):
    self.firstname = firstname
    self.lastname = lastname
    self.address = address

Appeler un constructeur

address = Address("Promenade de l'Alma", 50, 1200, "Woluwé-Saint-Lambert")
marchand = Person("Cédric", "Marchand", address)
flemal = Person("Clémence", "Flemal", address)
Construction d’objets

Objet et référence

print(marchand)
print(flemal)
<__main__.Person object at 0x109678748> <__main__.Person object at 0x109678780>

Variable d'instance

print(marchand.firstname)
print(flemal.address.number)
Cédric 50

Programmation orientée objet

État d'un objet

Identité d'un objet

prof = flemal
print(id(marchand))
print(id(flemal))
print(id(prof))
4302577224 4329799752 4329799752

Définir un vecteur dans le plan

class Vector:
  def __init__(self, x: float, y: float):
    self.x = x
    self.y = y

  def norm(self):
    return sqrt(self.x ** 2 + self.y ** 2)

u = Vector(1, -1)
print(u.norm())
1.4142135623730951

self

class Vector:
  def __init__(self, x: float, y: float):
    pass

  def norm(self):
    return sqrt(x ** 2 + y ** 2)

u = Vector(1, -1)
print(u.norm())
Traceback (most recent call last): File "program.py", line 38, in print(u.norm()) File "program.py", line 35, in norm return sqrt(x ** 2 + y ** 2) NameError: name 'x' is not defined

Représentation d’un objet

class Vector:
  def __init__(self, x: float, y: float):
    self.x = x
    self.y = y

  def __str__(self):
    return f'({self.x}, {self.y})'

u = Vector(1, -1)
print(u)
(1, -1)

Égalité

u = Vector(1, -1)
v = Vector(1, -1)
print(u == v)                  # False

Alias

u = Vector(1, -1)
v = u
print(u == v)                  # True

Surcharge d’opérateur

from typing import Self

class Vector:
  def __init__(self, x: float, y: float):
      self.x = x
      self.y = y

  def __eq__(self, other: Self):
      return self.x == other.x and self.y == other.y

  # ...

u = Vector(1, -1)
v = Vector(1, -1)
print(u == v)                  # True
print(u is v)                  # False

Surcharge d’opérateur

from typing import Self

class Vector:
  def __init__(self, x: float, y: float):
    self.x = x
    self.y = y

  def __add__(self, other: Self):
    return Vector(self.x + other.x, self.y + other.y)

  # ...

u = Vector(1, -1)
v = Vector(2, 1)
print(u + v)
(3, 0)

Composition d’objets

class Rectangle:
  def __init__(self, lowerleft: Vector, width: float, height: float, angle: float = 0):
    self.lowerleft = lowerleft
    self.width = width
    self.height = height
    self.angle = angle

p = Vector(1, -1)
r = Rectangle(p, 100, 50)
print(r.lowerleft)             # (1, -1)

Réutilisation de code

class Rectangle:
  # ...

  def __str__(self):
    return f"Rectangle en {self.lowerleft} + de longueur {self.width}" \
           f"et de hauteur {self.height} incliné de {self.angle} degrés"

r = Rectangle(Vector(1, -1), 100, 50)
print(r)
Rectangle en (1, -1) de longueur 100 et de hauteur 50 incliné de 0 degrés

Simplification

class Address:
  def __init__(self, street: str, number: int, zip: int, city: str):
    self.street = street
    self.number = number
    self.zip = zip
    self.city = city

  def display(self):
    return f"{self.street}, {self.number}\n{self.zip} {self.city}"

dataclass

from dataclasses import dataclass

@dataclass
class Address:
  street: str
  number: int
  zip: int
  city: str

address = Address("Promenade de l'Alma", 50, 1200, "Woluwé-Saint-Lambert")
print(address.zip) # 1200
print(address)     # Address(street="Promenade de l'Alma", number=50,
                   #         zip=1200, city='Woluwé-Saint-Lambert')

address2 = Address("Promenade de l'Alma", 50, 1200, "Woluwé-Saint-Lambert")
print(address == address2) # True

dataclass

from dataclasses import dataclass

@dataclass
class Address:
  street: str
  number: int
  zip: int
  city: str

  def display(self):
    return f"{self.street}, {self.number}\n{self.zip} {self.city}"
from dataclasses import dataclass

@dataclass
class Address:
  street: str
  number: int
  zip: int
  city: str

  def display(self):
    return f"{self.street}, {self.number}\n{self.zip} {self.city}"

  def __str__(self):
    return self.display()

dataclass

from dataclasses import dataclass

@dataclass
class Vector:
  x: float
  y: float

  def __add__(self, other: Self):
    return Vector(self.x + other.x, self.y + other.y)

  def __str__(self):
    return f'({self.x}, {self.y})'