Skip to content

Commit 29e711a

Browse files
authored
Merge pull request #24 from realpython/python-type-checking
Add materials for type checking article
2 parents eafada2 + 536e7ec commit 29e711a

File tree

9 files changed

+510
-0
lines changed

9 files changed

+510
-0
lines changed

python-type-checking/README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# The Ultimate Guide to Python Type Checking
2+
3+
Code examples from the [The Ultimate Guide to Python Type Checking](https://realpython.com/python-type-checking/) tutorial on [Real Python](https://realpython.com/).
4+

python-type-checking/dogs_001.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# dogs.py
2+
3+
from datetime import date
4+
5+
6+
class Animal:
7+
def __init__(self, name: str, birthday: date) -> None:
8+
self.name = name
9+
self.birthday = birthday
10+
11+
@classmethod
12+
def newborn(cls, name: str) -> "Animal":
13+
return cls(name, date.today())
14+
15+
def twin(self, name: str) -> "Animal":
16+
cls = self.__class__
17+
return cls(name, self.birthday)
18+
19+
20+
class Dog(Animal):
21+
def bark(self) -> None:
22+
print(f"{self.name} says woof!")
23+
24+
25+
fido = Dog.newborn("Fido")
26+
pluto = fido.twin("Pluto")
27+
fido.bark()
28+
pluto.bark()

python-type-checking/dogs_002.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# dogs.py
2+
3+
from datetime import date
4+
from typing import Type, TypeVar
5+
6+
TAnimal = TypeVar("TAnimal", bound="Animal")
7+
8+
9+
class Animal:
10+
def __init__(self, name: str, birthday: date) -> None:
11+
self.name = name
12+
self.birthday = birthday
13+
14+
@classmethod
15+
def newborn(cls: Type[TAnimal], name: str) -> TAnimal:
16+
return cls(name, date.today())
17+
18+
def twin(self: TAnimal, name: str) -> TAnimal:
19+
cls = self.__class__
20+
return cls(name, self.birthday)
21+
22+
23+
class Dog(Animal):
24+
def bark(self) -> None:
25+
print(f"{self.name} says woof!")
26+
27+
28+
fido = Dog.newborn("Fido")
29+
pluto = fido.twin("Pluto")
30+
fido.bark()
31+
pluto.bark()

python-type-checking/game_001.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# game.py
2+
3+
import random
4+
5+
SUITS = "♠ ♡ ♢ ♣".split()
6+
RANKS = "2 3 4 5 6 7 8 9 10 J Q K A".split()
7+
8+
9+
def create_deck(shuffle=False):
10+
"""Create a new deck of 52 cards"""
11+
deck = [(s, r) for r in RANKS for s in SUITS]
12+
if shuffle:
13+
random.shuffle(deck)
14+
return deck
15+
16+
17+
def deal_hands(deck):
18+
"""Deal the cards in the deck into four hands"""
19+
return (deck[0::4], deck[1::4], deck[2::4], deck[3::4])
20+
21+
22+
def play():
23+
"""Play a 4-player card game"""
24+
deck = create_deck(shuffle=True)
25+
names = "P1 P2 P3 P4".split()
26+
hands = {n: h for n, h in zip(names, deal_hands(deck))}
27+
28+
for name, cards in hands.items():
29+
card_str = " ".join(f"{s}{r}" for (s, r) in cards)
30+
print(f"{name}: {card_str}")
31+
32+
33+
if __name__ == "__main__":
34+
play()

python-type-checking/game_002.py

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
# game.py
2+
3+
import random
4+
from typing import List, Tuple
5+
6+
SUITS = "♠ ♡ ♢ ♣".split()
7+
RANKS = "2 3 4 5 6 7 8 9 10 J Q K A".split()
8+
9+
Card = Tuple[str, str]
10+
Deck = List[Card]
11+
12+
13+
def create_deck(shuffle: bool = False) -> Deck:
14+
"""Create a new deck of 52 cards"""
15+
deck = [(s, r) for r in RANKS for s in SUITS]
16+
if shuffle:
17+
random.shuffle(deck)
18+
return deck
19+
20+
21+
def deal_hands(deck: Deck) -> Tuple[Deck, Deck, Deck, Deck]:
22+
"""Deal the cards in the deck into four hands"""
23+
return (deck[0::4], deck[1::4], deck[2::4], deck[3::4])
24+
25+
26+
def choose(items):
27+
"""Choose and return a random item"""
28+
return random.choice(items)
29+
30+
31+
def player_order(names, start=None):
32+
"""Rotate player order so that start goes first"""
33+
if start is None:
34+
start = choose(names)
35+
start_idx = names.index(start)
36+
return names[start_idx:] + names[:start_idx]
37+
38+
39+
def play() -> None:
40+
"""Play a 4-player card game"""
41+
deck = create_deck(shuffle=True)
42+
names = "P1 P2 P3 P4".split()
43+
hands = {n: h for n, h in zip(names, deal_hands(deck))}
44+
start_player = choose(names)
45+
turn_order = player_order(names, start=start_player)
46+
47+
# Randomly play cards from each player's hand until empty
48+
while hands[start_player]:
49+
for name in turn_order:
50+
card = choose(hands[name])
51+
hands[name].remove(card)
52+
print(f"{name}: {card[0] + card[1]:<3} ", end="")
53+
print()
54+
55+
56+
if __name__ == "__main__":
57+
play()

python-type-checking/game_003.py

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
# game.py
2+
3+
import random
4+
import sys
5+
6+
7+
class Card:
8+
SUITS = "♠ ♡ ♢ ♣".split()
9+
RANKS = "2 3 4 5 6 7 8 9 10 J Q K A".split()
10+
11+
def __init__(self, suit, rank):
12+
self.suit = suit
13+
self.rank = rank
14+
15+
def __repr__(self):
16+
return f"{self.suit}{self.rank}"
17+
18+
19+
class Deck:
20+
def __init__(self, cards):
21+
self.cards = cards
22+
23+
@classmethod
24+
def create(cls, shuffle=False):
25+
"""Create a new deck of 52 cards"""
26+
cards = [Card(s, r) for r in Card.RANKS for s in Card.SUITS]
27+
if shuffle:
28+
random.shuffle(cards)
29+
return cls(cards)
30+
31+
def deal(self, num_hands):
32+
"""Deal the cards in the deck into a number of hands"""
33+
cls = self.__class__
34+
return tuple(cls(self.cards[i::num_hands]) for i in range(num_hands))
35+
36+
37+
class Player:
38+
def __init__(self, name, hand):
39+
self.name = name
40+
self.hand = hand
41+
42+
def play_card(self):
43+
"""Play a card from the player's hand"""
44+
card = random.choice(self.hand.cards)
45+
self.hand.cards.remove(card)
46+
print(f"{self.name}: {card!r:<3} ", end="")
47+
return card
48+
49+
50+
class Game:
51+
def __init__(self, *names):
52+
"""Set up the deck and deal cards to 4 players"""
53+
deck = Deck.create(shuffle=True)
54+
self.names = (list(names) + "P1 P2 P3 P4".split())[:4]
55+
self.hands = {
56+
n: Player(n, h) for n, h in zip(self.names, deck.deal(4))
57+
}
58+
59+
def play(self):
60+
"""Play a card game"""
61+
start_player = random.choice(self.names)
62+
turn_order = self.player_order(start=start_player)
63+
64+
# Play cards from each player's hand until empty
65+
while self.hands[start_player].hand.cards:
66+
for name in turn_order:
67+
self.hands[name].play_card()
68+
print()
69+
70+
def player_order(self, start=None):
71+
"""Rotate player order so that start goes first"""
72+
if start is None:
73+
start = random.choice(self.names)
74+
start_idx = self.names.index(start)
75+
return self.names[start_idx:] + self.names[:start_idx]
76+
77+
78+
if __name__ == "__main__":
79+
# Read player names from command line
80+
player_names = sys.argv[1:]
81+
game = Game(*player_names)
82+
game.play()

0 commit comments

Comments
 (0)