Магические методы контейнеров
Часть раздела 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__ позволяют вашим пользовательским классам вести себя как встроенные контейнеры. Это обеспечивает интуитивно понятную индексацию, итерацию и проверку на вхождение для ваших объектов.
Задание
СреднеВ этом испытании вы реализуете класс Deck, который имитирует колоду игральных карт с полным набором функций и соблюдением соглашений Python.
Вам нужно отредактировать только файл deck.py. Следуйте комментариям TODO в коде, которые помогут вам реализовать:
- Инициализацию стандартной колоды из 52 карт (используя такие строки, как "2H", "KD", "AS")
- Поддержку встроенных операций Python:
- Индексацию (
deck[0]) - Проверку длины (
len(deck)) - Итерацию (
for card in deck) - Проверку на вхождение (
"AS" in deck)
- Индексацию (
- Метод
shuffleдля перемешивания карт в случайном порядке
Шпаргалка
Магические методы контейнеров позволяют вашим классам вести себя как встроенные контейнеры (списки, словари и т. д.). Они обеспечивают индексацию, проверку длины и итерацию для ваших пользовательских объектов.
Основные магические методы контейнеров:
__len__()— активирует функциюlen()__getitem__()— активирует индексацию для получения значений__setitem__()— активирует индексацию для присваивания значений__iter__()— активирует итерацию__contains__()— активирует операторin
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Примеры использования:
my_list = CustomList([1, 2, 3, 4])
# Проверка длины
print(len(my_list)) # 4
# Индексация
print(my_list[2]) # 3
my_list[1] = 10
# Проверка на вхождение
print(3 in my_list) # True
# Итерация
for item in my_list:
print(item)Попробуйте сами
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")В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Object Oriented Programming
1Основы ООП
Внешние файлыВведение в ООПКлассы и объектыПараметр selfМетодыАтрибутыМетод-конструктор (__init__)Повторение — Простой калькулятор4Наследование
Базовое наследованиеФункция super()Переопределение методовМножественное наследованиеПорядок разрешения методовИтоги — Иерархия сотрудников7Специальные методы
Введение в магические методыПерегрузка операторовМагические методы контейнеровПовторение — Пользовательский список10Паттерны проектирования. Часть 1
Введение в паттерны проектированияПаттерн SingletonПаттерн FactoryПаттерн ObserverПаттерн Strategy5Полиморфизм
Снова о переопределении методовУтиная типизацияАбстрактные классы и методыПроектирование интерфейсовИтоги — Калькулятор фигур8Продвинутые концепции ООП
Композиция против наследованияМиксиныСтатические методы и методы классаДекораторы классовМенеджеры контекста11Паттерны проектирования. Часть 2
Паттерн КомандаПаттерн АдаптерПаттерн ДекораторПаттерн Шаблонный методПаттерн СостояниеПаттерн Компоновщик3Свойства классов
Переменные экземпляра и классаДекораторы свойствПриватные атрибутыИтоги — Менеджер банковского счета6Инкапсуляция
Public, Protected, Private члены классаМодификаторы доступаСокрытие данныхДекораторы property: продвинутый уровеньИтоги: Система учета студентов12Проект: Управление библиотекой
Обзор проектаКлассы Book и User