Skip to content

Commit 1df34f2

Browse files
committed
ch05: new chapter
1 parent b74ea52 commit 1df34f2

25 files changed

+640
-0
lines changed

05-record-like/cards.doctest

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
>>> from cards import Card
2+
>>> helen = Card('Q', 'hearts')
3+
>>> helen
4+
Card(rank='Q', suit='hearts')
5+
6+
>>> cards = [Card(r, s) for s in Card.suits for r in Card.ranks]
7+
>>> cards[:3]
8+
[Card(rank='2', suit='spades'), Card(rank='3', suit='spades'), Card(rank='4', suit='spades')]
9+
>>> sorted(cards)[:3]
10+
[Card(rank='2', suit='clubs'), Card(rank='2', suit='diamonds'), Card(rank='2', suit='hearts')]
11+
12+
>>> from cards_enum import Card, Suit, Rank
13+
>>> helen = Card('Q', 'hearts')
14+
>>> helen
15+
Card(rank='Q', suit='hearts')
16+
17+
>>> cards = [Card(r, s) for s in Suit for r in Rank]
18+
>>> cards[:3]
19+
[Card(rank='2', suit='spades'), Card(rank='3', suit='spades'), Card(rank='4', suit='spades')]
20+
>>> sorted(cards)[:3]
21+
[Card(rank='2', suit='clubs'), Card(rank='2', suit='diamonds'), Card(rank='2', suit='hearts')]
22+
>>> for card in cards[12::13]: print(card)

05-record-like/cards.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
from dataclasses import dataclass
2+
3+
@dataclass(order=True)
4+
class Card:
5+
rank: str
6+
suit: str
7+
8+
ranks = [str(n) for n in range(2, 10)] + list('JQKA')
9+
suits = 'spades diamonds clubs hearts'.split()

05-record-like/cards_enum.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
from dataclasses import dataclass
2+
import enum
3+
4+
Suit = enum.IntEnum('Suit', 'spades diamonds clubs hearts')
5+
Rank = enum.Enum('Rank', [str(n) for n in range(2, 10)] + list('JQKA'))
6+
7+
@dataclass(order=True)
8+
class Card:
9+
rank: Suit
10+
suit: Rank
11+
12+
def __str__(self):
13+
glyphs = [chr(x) for x in range(0x2660, 0x2664)]
14+
return f'{self.rank} of {glyphs[self.suit-1]}'
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
"""
2+
``Coordinate``: a simple class with a custom ``__str__``::
3+
4+
>>> moscow = Coordinate(55.756, 37.617)
5+
>>> print(moscow) # doctest:+ELLIPSIS
6+
<coordinates.Coordinate object at 0x...>
7+
"""
8+
9+
# tag::COORDINATE[]
10+
class Coordinate:
11+
12+
def __init__(self, lat, long):
13+
self.lat = lat
14+
self.long = long
15+
16+
# end::COORDINATE[]

05-record-like/dataclass/club.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
from dataclasses import dataclass, field
2+
3+
4+
@dataclass
5+
class ClubMember:
6+
7+
name: str
8+
guests: list = field(default_factory=list)
9+
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
from dataclasses import dataclass, field
2+
from typing import List # <1>
3+
4+
@dataclass
5+
class ClubMember:
6+
7+
name: str
8+
guests: List[str] = field(default_factory=list) # <2>
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
from dataclasses import dataclass
2+
3+
# tag::CLUBMEMBER[]
4+
@dataclass
5+
class ClubMember:
6+
7+
name: str
8+
guests: list = []
9+
# end::CLUBMEMBER[]
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
"""
2+
``Coordinate``: simple class decorated with ``dataclass`` and a custom ``__str__``::
3+
4+
>>> moscow = Coordinate(55.756, 37.617)
5+
>>> print(moscow)
6+
55.8°N, 37.6°E
7+
8+
"""
9+
10+
# tag::COORDINATE[]
11+
12+
from dataclasses import dataclass
13+
14+
@dataclass(frozen=True)
15+
class Coordinate:
16+
17+
lat: float
18+
long: float
19+
20+
def __str__(self):
21+
ns = 'N' if self.lat >= 0 else 'S'
22+
we = 'E' if self.long >= 0 else 'W'
23+
return f'{abs(self.lat):.1f}°{ns}, {abs(self.long):.1f}°{we}'
24+
# end::COORDINATE[]
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
# tag::DOCTESTS[]
2+
"""
3+
``HackerClubMember`` objects accept an optional ``handle`` argument::
4+
5+
>>> anna = HackerClubMember('Anna Ravenscroft', handle='AnnaRaven')
6+
>>> anna
7+
HackerClubMember(name='Anna Ravenscroft', guests=[], handle='AnnaRaven')
8+
9+
If ``handle`` is ommitted, it's set to the first part of the member's name::
10+
11+
>>> leo = HackerClubMember('Leo Rochael')
12+
>>> leo
13+
HackerClubMember(name='Leo Rochael', guests=[], handle='Leo')
14+
15+
Members must have a unique handle. The following ``leo2`` will not be created,
16+
because its ``handle`` would be 'Leo', which was taken by ``leo``::
17+
18+
>>> leo2 = HackerClubMember('Leo DaVinci')
19+
Traceback (most recent call last):
20+
...
21+
ValueError: handle 'Leo' already exists.
22+
23+
To fix, ``leo2`` must be created with an explicit ``handle``::
24+
25+
>>> leo2 = HackerClubMember('Leo DaVinci', handle='Neo')
26+
>>> leo2
27+
HackerClubMember(name='Leo DaVinci', guests=[], handle='Neo')
28+
"""
29+
# end::DOCTESTS[]
30+
31+
# tag::HACKERCLUB[]
32+
from dataclasses import dataclass
33+
from club import ClubMember
34+
35+
@dataclass
36+
class HackerClubMember(ClubMember): # <1>
37+
38+
all_handles = set() # <2>
39+
40+
handle: str = '' # <3>
41+
42+
def __post_init__(self):
43+
cls = self.__class__ # <4>
44+
if self.handle == '': # <5>
45+
self.handle = self.name.split()[0]
46+
if self.handle in cls.all_handles: # <6>
47+
msg = f'handle {self.handle!r} already exists.'
48+
raise ValueError(msg)
49+
cls.all_handles.add(self.handle) # <7>
50+
# end::HACKERCLUB[]
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
# tag::DOCTESTS[]
2+
"""
3+
``HackerClubMember`` objects can be created with a ``name`` and an optional ``handle``::
4+
5+
>>> anna = HackerClubMember('Anna Ravenscroft', handle='AnnaRaven')
6+
>>> anna
7+
HackerClubMember(name='Anna Ravenscroft', guests=[], handle='AnnaRaven')
8+
9+
If ``handle`` is ommitted, it's set to the first part of the member's name::
10+
11+
>>> leo = HackerClubMember('Leo Rochael')
12+
>>> leo
13+
HackerClubMember(name='Leo Rochael', guests=[], handle='Leo')
14+
15+
Members must have a unique handle. This ``leo2`` will not be created,
16+
because its ``handle`` would be 'Leo', which was taken by ``leo``::
17+
18+
>>> leo2 = HackerClubMember('Leo DaVinci')
19+
Traceback (most recent call last):
20+
...
21+
ValueError: handle 'Leo' already exists.
22+
23+
To fix, ``leo2`` must be created with an explicit ``handle``::
24+
25+
>>> leo2 = HackerClubMember('Leo DaVinci', handle='Neo')
26+
>>> leo2
27+
HackerClubMember(name='Leo DaVinci', guests=[], handle='Neo')
28+
"""
29+
# end::DOCTESTS[]
30+
31+
# tag::HACKERCLUB[]
32+
from dataclasses import dataclass
33+
from typing import ClassVar, Set
34+
from club import ClubMember
35+
36+
@dataclass
37+
class HackerClubMember(ClubMember):
38+
39+
all_handles: ClassVar[Set[str]] = set()
40+
41+
handle: str = ''
42+
43+
def __post_init__(self):
44+
cls = self.__class__
45+
if self.handle == '':
46+
self.handle = self.name.split()[0]
47+
if self.handle in cls.all_handles:
48+
msg = f'handle {self.handle!r} already exists.'
49+
raise ValueError(msg)
50+
cls.all_handles.add(self.handle)
51+
# end::HACKERCLUB[]

0 commit comments

Comments
 (0)