컨테이너 매직 메소드
Coddy Python 여정의 Object Oriented Programming 섹션에 포함된 레슨 — 64개 중 35번째.
컨테이너 매직 메서드를 사용하면 클래스가 내장 컨테이너(리스트, 딕셔너리 등)처럼 동작할 수 있습니다. 이를 통해 사용자 정의 객체에서 인덱싱, 길이 확인 및 반복이 가능해집니다.
다음은 컨테이너 매직 메서드를 포함한 클래스의 예입니다:
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__와 같은 컨테이너 매직 메서드들은 사용자 정의 클래스가 내장 컨테이너처럼 동작하도록 만듭니다. 이는 객체에 대해 직관적인 인덱싱, 반복 및 멤버십 테스트를 제공합니다.
챌린지
중급이 챌린지에서는 포괄적인 기능과 적절한 Python 규칙을 갖춘 카드 덱을 시뮬레이션하는 Deck 클래스를 구현합니다.
deck.py 파일만 수정하면 됩니다. 코드 내의 TODO 주석을 따라 다음 사항들을 구현하세요:
- 표준 52장 카드 덱 초기화 ("2H", "KD", "AS"와 같은 문자열 사용)
- Python의 내장 연산 지원:
- 인덱싱 (
deck[0]) - 길이 확인 (
len(deck)) - 반복 (
for card in deck) - 멤버십 테스트 (
"AS" in deck)
- 인덱싱 (
- 카드 순서를 무작위로 섞는
shuffle메서드
치트 시트
컨테이너 매직 메서드를 사용하면 클래스가 내장 컨테이너(리스트, 딕셔너리 등)처럼 동작하도록 만들 수 있습니다. 이를 통해 사용자 정의 객체에서 인덱싱, 길이 확인 및 반복(iteration) 기능을 사용할 수 있습니다.
주요 컨테이너 매직 메서드:
__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()
# 반복(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}"
# 여러 번의 반복 테스트
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"
# 반복(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"
# 셔플 테스트 (순서가 변경되는지 기본 확인)
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")이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.