Menu
Coddy logo textTech

Магические методы контейнеров

Часть раздела Object Oriented Programming путешествия по Python на Coddy. Урок 35 из 64.

Магические методы контейнеров позволяют вашим классам вести себя как встроенные контейнеры (списки, словари и т. д.). Они позволяют использовать индексацию, проверку длины и итерацию для ваших пользовательских объектов.

Вот пример класса с магическими методами контейнера:

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

Метод __len__ делает так, чтобы len() работал:

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

Метод __getitem__ позволяет использовать индексацию для получения элементов:

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

Метод __setitem__ позволяет использовать индексацию для присваивания:

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

Метод __contains__ позволяет использовать оператор in:

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

Метод __iter__ позволяет выполнять итерацию:

for item in my_list:
    print(item)

Вывод:

4
3
1
10
True
False
1
10
3
4

Ключевой момент: Магические методы контейнеров, такие как __len__, __getitem__, __setitem__, __iter__ и __contains__ позволяют вашим пользовательским классам вести себя как встроенные контейнеры. Это обеспечивает интуитивно понятную индексацию, итерацию и проверку на вхождение для ваших объектов.

challenge icon

Задание

Средне

В этом испытании вы реализуете класс Deck, который имитирует колоду игральных карт с полным набором функций и соблюдением соглашений Python.

Вам нужно отредактировать только файл deck.py. Следуйте комментариям TODO в коде, которые помогут вам реализовать:

  • Инициализацию стандартной колоды из 52 карт (используя такие строки, как "2H", "KD", "AS")
  • Поддержку встроенных операций Python:
    • Индексацию (deck[0])
    • Проверку длины (len(deck))
    • Итерацию (for card in deck)
    • Проверку на вхождение ("AS" in deck)
  • Метод shuffle для перемешивания карт в случайном порядке

Попробуйте сами

from deck import Deck

# Комплексный обработчик тестовых случаев
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()
    
    # Проверка доступа к первой и последней карте
    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"
    
    # Проверка отрицательной индексации
    assert deck[-1] == deck[51], "Negative indexing should work correctly"
    
    # Проверка доступа за пределами диапазона
    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"
    
    # Проверка существования конкретных карт
    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]
    
    # Первая перетасовка
    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"
    
    # Скорее всего, порядок изменился (хотя есть крошечная вероятность, что нет)
    different_order = (original_order != first_shuffle)
    
    # Вторая перетасовка для полной уверенности
    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()
    
    # Проверка, что все валидные карты есть в колоде
    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"
    
    # Проверка, что невалидных карт нет в колоде
    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()
    
    # Проверка итерации
    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}"
    
    # Проверка нескольких итераций
    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!")

# Запуск соответствующего теста на основе ввода
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:
    # Тест по умолчанию - запуск оригинального набора тестов
    def test_deck():
        try:
            # Проверка инициализации и длины
            deck = Deck()
            assert len(deck) == 52, f"Deck should have 52 cards, but has {len(deck)}"
            
            # Проверка getitem
            first_card = deck[0]
            assert isinstance(first_card, str), f"Card should be a string, but got {type(first_card)}"
            
            # Проверка 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"
            
            # Проверка итерации
            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"
            
            # Проверка перетасовки (базовая проверка изменения порядка)
            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"
            
            # Проверка, что перетасовка не теряет карты
            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 iconПроверьте себя

В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.

Все уроки раздела Object Oriented Programming

Потренируйтесь самостоятельно: Онлайн-компилятор Python