Méthodes magiques de conteneur
Fait partie de la section Object Oriented Programming du Journey Python de Coddy — leçon 35 sur 64.
Les méthodes magiques de conteneur permettent à vos classes de se comporter comme des conteneurs intégrés (listes, dictionnaires, etc.). Elles permettent l'indexation, la vérification de la longueur et l'itération sur vos objets personnalisés.
Voici un exemple d'une classe avec des méthodes magiques de conteneur :
class CustomList:
def __init__(self, items):
self.items = items
def __len__(self):
return len(self.items)
def __getitem__(self, index):
return self.items[index]
def __setitem__(self, index, value):
self.items[index] = value
def __iter__(self):
return iter(self.items)
def __contains__(self, item):
return item in self.itemsLa méthode __len__ permet de faire fonctionner len() :
my_list = CustomList([1, 2, 3, 4])
print(len(my_list)) # 4La méthode __getitem__ permet l'indexation pour la récupération :
print(my_list[2]) # 3
print(my_list[0]) # 1La méthode __setitem__ permet l'indexation pour l'affectation :
my_list[1] = 10
print(my_list[1]) # 10La méthode __contains__ permet de faire fonctionner l'opérateur in :
print(3 in my_list) # True
print(100 in my_list) # FalseLa méthode __iter__ permet l'itération :
for item in my_list:
print(item)Sortie :
4
3
1
10
True
False
1
10
3
4Point clé : Les méthodes magiques de conteneur comme __len__, __getitem__, __setitem__, __iter__ et __contains__ permettent à vos classes personnalisées de se comporter comme des conteneurs intégrés. Cela permet l'indexation, l'itération et les tests d'appartenance intuitifs pour vos objets.
Défi
MoyenDans ce défi, vous allez implémenter une classe Deck qui simule un jeu de cartes à jouer avec des fonctionnalités complètes et en respectant les conventions Python appropriées.
Vous n'avez qu'à modifier le fichier deck.py. Suivez les commentaires TODO dans le code qui vous guideront pour implémenter :
- L'initialisation d'un jeu standard de 52 cartes (en utilisant des chaînes comme "2H", "KD", "AS")
- Le support des opérations intégrées de Python :
- L'indexation (
deck[0]) - La vérification de la longueur (
len(deck)) - L'itération (
for card in deck) - Le test d'appartenance (
"AS" in deck)
- L'indexation (
- Une méthode
shufflepour randomiser l'ordre des cartes
Aide-mémoire
Les méthodes magiques de conteneur permettent à vos classes de se comporter comme des conteneurs intégrés (listes, dictionnaires, etc.). Elles permettent l'indexation, la vérification de la longueur et l'itération sur vos objets personnalisés.
Méthodes magiques de conteneur clés :
__len__()- active la fonctionlen()__getitem__()- active l'indexation pour la récupération__setitem__()- active l'indexation pour l'assignation__iter__()- active l'itération__contains__()- active l'opérateurin
class CustomList:
def __init__(self, items):
self.items = items
def __len__(self):
return len(self.items)
def __getitem__(self, index):
return self.items[index]
def __setitem__(self, index, value):
self.items[index] = value
def __iter__(self):
return iter(self.items)
def __contains__(self, item):
return item in self.itemsExemples d'utilisation :
my_list = CustomList([1, 2, 3, 4])
# Vérification de la longueur
print(len(my_list)) # 4
# Indexation
print(my_list[2]) # 3
my_list[1] = 10
# Test d'appartenance
print(3 in my_list) # True
# Itération
for item in my_list:
print(item)Essayez vous-même
from deck import Deck
# Gestionnaire de cas de test complet
test_case = input()
def test_basic_functionality():
deck = Deck()
assert len(deck) == 52, f"Deck should have 52 cards, but has {len(deck)}"
first_card = deck[0]
assert isinstance(first_card, str), f"Card should be a string, but got {type(first_card)}"
assert "AS" in deck, "Ace of Spades should be in the deck"
assert "XY" not in deck, "XY is not a valid card and should not be in the deck"
cards = [card for card in deck]
assert len(cards) == 52, f"Iteration should yield 52 cards, but got {len(cards)}"
original_first_five = [deck[i] for i in range(5)]
deck.shuffle()
shuffled_first_five = [deck[i] for i in range(5)]
assert original_first_five != shuffled_first_five or len(deck) <= 5, "Shuffle should change card order"
print("Basic functionality tests passed!")
def test_edge_cases():
deck = Deck()
# Test de l'accès à la première et à la dernière carte
first_card = deck[0]
last_card = deck[51]
assert isinstance(first_card, str) and isinstance(last_card, str), "First and last cards should be strings"
# Test de l'indexation négative
assert deck[-1] == deck[51], "Negative indexing should work correctly"
# Test de l'accès hors limites
try:
invalid_card = deck[52]
print("Test failed: Should raise IndexError for out of bounds access")
except IndexError:
print("Edge case test passed: IndexError raised for out of bounds access")
print("Edge case tests passed!")
def test_card_uniqueness():
deck = Deck()
cards = [card for card in deck]
unique_cards = set(cards)
assert len(unique_cards) == 52, f"All cards should be unique, but found {len(unique_cards)} unique cards"
# Vérifier que des cartes spécifiques existent
expected_cards = ["2H", "10S", "KD", "AC"]
for card in expected_cards:
assert card in deck, f"Expected card {card} not found in deck"
print("Card uniqueness tests passed!")
def test_shuffle_behavior():
deck = Deck()
original_order = [card for card in deck]
# Premier mélange
deck.shuffle()
first_shuffle = [card for card in deck]
assert len(first_shuffle) == 52, "Shuffle should preserve all 52 cards"
assert set(first_shuffle) == set(original_order), "Shuffle should not add or remove cards"
# Très probablement, l'ordre a changé (bien qu'il y ait une infime probabilité que non)
different_order = (original_order != first_shuffle)
# Deuxième mélange pour être tout à fait sûr
deck.shuffle()
second_shuffle = [card for card in deck]
different_order_2 = (first_shuffle != second_shuffle)
assert different_order or different_order_2, "Multiple shuffles should change the order"
print("Shuffle behavior tests passed!")
def test_contains_behavior():
deck = Deck()
# Vérifier que toutes les cartes valides sont dans le paquet
suits = ['H', 'D', 'C', 'S']
ranks = ['2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K', 'A']
for suit in suits:
for rank in ranks:
card = rank + suit
assert card in deck, f"Valid card {card} should be in the deck"
# Vérifier que les cartes invalides ne sont pas dans le paquet
invalid_cards = ["1H", "11S", "XD", "AX", "JX", ""]
for card in invalid_cards:
assert card not in deck, f"Invalid card {card} should not be in the deck"
print("Contains behavior tests passed!")
def test_iteration_behavior():
deck = Deck()
# Test de l'itération
card_count = 0
for card in deck:
card_count += 1
assert isinstance(card, str), f"Each card should be a string, but got {type(card)}"
assert card_count == 52, f"Iteration should yield 52 cards, but got {card_count}"
# Test de plusieurs itérations
first_iteration = [card for card in deck]
second_iteration = [card for card in deck]
assert first_iteration == second_iteration, "Multiple iterations should yield the same order"
print("Iteration behavior tests passed!")
# Exécuter le test approprié en fonction de l'entrée
if test_case == "basic_functionality":
test_basic_functionality()
elif test_case == "edge_cases":
test_edge_cases()
elif test_case == "card_uniqueness":
test_card_uniqueness()
elif test_case == "shuffle_behavior":
test_shuffle_behavior()
elif test_case == "contains_behavior":
test_contains_behavior()
elif test_case == "iteration_behavior":
test_iteration_behavior()
else:
# Test par défaut - exécuter la suite de tests originale
def test_deck():
try:
# Test de l'initialisation et de la longueur
deck = Deck()
assert len(deck) == 52, f"Deck should have 52 cards, but has {len(deck)}"
# Test de getitem
first_card = deck[0]
assert isinstance(first_card, str), f"Card should be a string, but got {type(first_card)}"
# Test de contains
assert "AS" in deck, "Ace of Spades should be in the deck"
assert "XY" not in deck, "XY is not a valid card and should not be in the deck"
# Test de l'itération
cards = [card for card in deck]
assert len(cards) == 52, f"Iteration should yield 52 cards, but got {len(cards)}"
assert len(set(cards)) == 52, "All cards in the deck should be unique"
# Test du mélange (vérification de base que l'ordre change)
original_first_five = [deck[i] for i in range(5)]
deck.shuffle()
shuffled_first_five = [deck[i] for i in range(5)]
assert original_first_five != shuffled_first_five or len(deck) <= 5, "Shuffle should change card order"
# Vérifier que le mélange ne perd pas de cartes
assert len(deck) == 52, f"Deck should still have 52 cards after shuffle, but has {len(deck)}"
print("All tests passed!")
except AssertionError as e:
print(f"Test failed: {e}")
test_deck()
print("Tests completed")Cette leçon comprend un petit quiz. Commencez la leçon pour y répondre et suivre votre progression.
Toutes les leçons de Object Oriented Programming
1Les fondamentaux de la POO
Fichiers externesIntroduction à la POOClasses vs ObjetsLe paramètre selfMéthodesAttributsMéthode constructeur (__init__)Récapitulatif - Calculatrice simple4Héritage
Héritage de baseLa fonction super()Redéfinition de méthodeHéritage multipleOrdre de résolution des méthodesRécapitulatif - Hiérarchie des employés7Méthodes spéciales
Introduction aux méthodes magiquesSurcharge d'opérateursMéthodes magiques de conteneurRécapitulatif - Liste personnalisée10Patrons de conception, Partie 1
Introduction aux patrons de conceptionPatron SingletonPatron FactoryPatron ObserverPatron Strategy2Décorateurs
Introduction aux décorateursDécorateur de propriétéDécorateur de méthode statiqueDécorateur de méthode de classe5Polymorphisme
Retour sur la redéfinition de méthodeDuck TypingClasses et méthodes abstraitesConception d'interfacesRécapitulatif - Calculateur de formes8Concepts avancés de la POO
Composition vs HéritageMixinsMéthodes statiques et de classeDécorateurs de classeGestionnaires de contexte11Patrons de conception, Partie 2
Patron CommandePatron AdaptateurPatron DécorateurPatron Méthode TemplatePatron ÉtatPatron Composite3Propriétés de classe
Variables d'instance vs variables de classeDécorateurs de propriétéAttributs privésRécapitulatif - Gestionnaire de compte bancaire