Menu
Coddy logo textTech

Magic Methods für Container

Teil des Abschnitts Object Oriented Programming der Python-Journey von Coddy — Lektion 35 von 64.

Container-Magic-Methoden ermöglichen es Ihren Klassen, sich wie integrierte Container (Listen, Dictionaries, etc.) zu verhalten. Sie ermöglichen Indizierung, Längenprüfung und Iteration für Ihre benutzerdefinierten Objekte.

Hier ist ein Beispiel für eine Klasse mit Container-Magic-Methoden:

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.items

Die __len__-Methode lässt len() funktionieren:

my_list = CustomList([1, 2, 3, 4])
print(len(my_list))  # 4

Die __getitem__ Methode ermöglicht die Indizierung zum Abrufen:

print(my_list[2])    # 3
print(my_list[0])    # 1

Die Methode __setitem__ ermöglicht die Indizierung für Zuweisungen:

my_list[1] = 10
print(my_list[1])    # 10

Die __contains__-Methode sorgt dafür, dass der in-Operator funktioniert:

print(3 in my_list)     # True
print(100 in my_list)   # False

Die __iter__-Methode ermöglicht die Iteration:

for item in my_list:
    print(item)

Ausgabe:

4
3
1
10
True
False
1
10
3
4

Kernpunkt: Magische Container-Methoden wie __len__, __getitem__, __setitem__, __iter__ und __contains__ lassen Ihre benutzerdefinierten Klassen sich wie integrierte Container verhalten. Dies ermöglicht intuitives Indizieren, Iterieren und Prüfen auf Mitgliedschaft für Ihre Objekte.

challenge icon

Aufgabe

Mittel

In dieser Herausforderung implementierst du eine Deck-Klasse, die ein Kartenspiel mit umfassender Funktionalität und unter Einhaltung der Python-Konventionen simuliert.

Du musst nur die Datei deck.py bearbeiten. Folge den TODO-Kommentaren im Code, die dich durch die Implementierung führen:

  • Initialisierung eines Standard-Kartenspiels mit 52 Karten (unter Verwendung von Strings wie "2H", "KD", "AS")
  • Unterstützung für Pythons integrierte Operationen:
    • Indizierung (deck[0])
    • Längenprüfung (len(deck))
    • Iteration (for card in deck)
    • Mitgliedschaftsprüfung ("AS" in deck)
  • Eine shuffle-Methode, um die Reihenfolge der Karten zu randomisieren

Spickzettel

Container-Magie-Methoden ermöglichen es Ihren Klassen, sich wie integrierte Container (Listen, Wörterbücher usw.) zu verhalten. Sie ermöglichen Indizierung, Längenprüfung und Iteration für Ihre benutzerdefinierten Objekte.

Wichtige Container-Magie-Methoden:

  • __len__() - ermöglicht die len()-Funktion
  • __getitem__() - ermöglicht die Indizierung zum Abrufen
  • __setitem__() - ermöglicht die Indizierung zur Zuweisung
  • __iter__() - ermöglicht die Iteration
  • __contains__() - ermöglicht den in-Operator
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.items

Anwendungsbeispiele:

my_list = CustomList([1, 2, 3, 4])

# Längenprüfung
print(len(my_list))  # 4

# Indizierung
print(my_list[2])    # 3
my_list[1] = 10

# Mitgliedschaftsprüfung
print(3 in my_list)     # True

# Iteration
for item in my_list:
    print(item)

Probier es selbst

from deck import Deck

# Umfassender Testfall-Handler
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()
    
    # Teste Zugriff auf die erste und letzte Karte
    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"
    
    # Teste negative Indizierung
    assert deck[-1] == deck[51], "Negative indexing should work correctly"
    
    # Teste Zugriff außerhalb der Grenzen
    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"
    
    # Überprüfe, ob bestimmte Karten existieren
    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]
    
    # Erstes Mischen
    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"
    
    # Höchstwahrscheinlich hat sich die Reihenfolge geändert (obwohl eine winzige Wahrscheinlichkeit besteht, dass dies nicht der Fall war)
    different_order = (original_order != first_shuffle)
    
    # Zweites Mischen, um ganz sicher zu gehen
    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()
    
    # Teste, ob alle gültigen Karten im Deck sind
    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"
    
    # Teste, ob ungültige Karten nicht im Deck sind
    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()
    
    # Teste Iteration
    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}"
    
    # Teste mehrfache Iterationen
    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!")

# Führe den entsprechenden Test basierend auf der Eingabe aus
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:
    # Standardtest - führe die ursprüngliche Testsuite aus
    def test_deck():
        try:
            # Teste Initialisierung und Länge
            deck = Deck()
            assert len(deck) == 52, f"Deck should have 52 cards, but has {len(deck)}"
            
            # Teste getitem
            first_card = deck[0]
            assert isinstance(first_card, str), f"Card should be a string, but got {type(first_card)}"
            
            # Teste 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"
            
            # Teste Iteration
            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"
            
            # Teste Mischen (grundlegende Prüfung, ob sich die Reihenfolge ändert)
            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"
            
            # Überprüfe, ob beim Mischen keine Karten verloren gehen
            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")
quiz iconTeste dich selbst

Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.

Alle Lektionen in Object Oriented Programming