diff --git a/python_sandbox/python_sandbox/fluent_python/README.md b/python_sandbox/python_sandbox/fluent_python/README.md new file mode 100644 index 00000000..d8234b73 --- /dev/null +++ b/python_sandbox/python_sandbox/fluent_python/README.md @@ -0,0 +1,38 @@ +# How to run tests? +python -m pytest + +# Python Data Model + +The first thing to know about special methods (__len__ etc) is that they are meant +to be called the python interpreter, and not by you. You don't write `obj.__len__()`. +You write `len(obj)`. + +More often than not, the special method call is implicit. For example, the statement +`for i in x:` actually causes the invocation of `iter(x)`, which in turn may call +`x.__iter__()` if that's available, or use `x.__getitem__()` as a fallback. + +You should be implementing them more often than invoking them explicitly. + +## __repr__ vs __str__ +https://stackoverflow.com/questions/1436703/what-is-the-difference-between-str-and-repr + +* The default implementation is useless (it’s hard to think of one which wouldn’t be, but yeah) +* __repr__ goal is to be unambiguous +* __str__ goal is to be readable + +## Collections API + +The "Collection" ABC (abstract base class) has 3 parents: +* Iterable (`__iter__`): to support the `for` construct +* Sized (`__len__`): to support the `len` function +* Container (`__contains__`): to support the `in` operator + +The "Collection" ABC (abstract base class) has 3 children: +* Sequence (__getitem__, __iter__, etc) +* Mapping (__getitem__, __contains__, etc) +* Set + +There's also a `Reversible` ABC and `Sequence` is a child of it. + +Note that python doesn't require concrete classes to actually inherit from any of +these ABCs. Any class that implements `__len__` satisfies the `Sized` interface. diff --git a/python_sandbox/python_sandbox/fluent_python/__init__.py b/python_sandbox/python_sandbox/fluent_python/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/python_sandbox/python_sandbox/fluent_python/chapter1/__init__.py b/python_sandbox/python_sandbox/fluent_python/chapter1/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/python_sandbox/python_sandbox/fluent_python/chapter1/cards.py b/python_sandbox/python_sandbox/fluent_python/chapter1/cards.py deleted file mode 100644 index 3e32c633..00000000 --- a/python_sandbox/python_sandbox/fluent_python/chapter1/cards.py +++ /dev/null @@ -1,62 +0,0 @@ -import collections -from random import choice - -Card = collections.namedtuple('Card', ['rank', 'suit']) - - -class FrenchDeck: - ranks = [str(n) for n in range(2, 11)] + list('JQKA') - suits = 'spades diamonds clubs hearts'.split() - - def __init__(self): - self._cards = [Card(rank, suit) for suit in self.suits - for rank in self.ranks] - - def __len__(self): - return len(self._cards) - - def __getitem__(self, position): - return self._cards[position] - - -suit_values = dict(spades=3, hearts=2, diamonds=1, clubs=0) - - -def spades_high(card): - rank_value = FrenchDeck.ranks.index(card.rank) - return rank_value * len(suit_values) + suit_values[card.suit] - - -def main(): - beer_card = Card('7', 'diamonds') - print(beer_card) - - deck = FrenchDeck() - print('Number of cards', len(deck)) - print('First card', deck[0]) - print('Last card', deck[-1]) - # choice works with a sequence and FrenchDeck is a sequence because it implements - # both __len__ and __getitem__ - print('Pick a random card', choice(deck)) - print('Pick a random card', choice(deck)) - - # slicing works - print('First 3 cards', deck[:3]) - print('Last 3 cards', deck[-3:]) - - # iteration works too because of __getitem__ - for card in deck: - print(card) - - # in works too - # If a collection has no __contains__ method, the "in" operator does a sequential scan - print(Card('Q', 'hearts') in deck) - print(Card('7', 'beasts') in deck) - - # sort cards - for card in sorted(deck, key=spades_high): - print(card) - - -if __name__ == '__main__': - main() diff --git a/python_sandbox/python_sandbox/fluent_python/python_data_model/cards.py b/python_sandbox/python_sandbox/fluent_python/python_data_model/cards.py new file mode 100644 index 00000000..02eca77f --- /dev/null +++ b/python_sandbox/python_sandbox/fluent_python/python_data_model/cards.py @@ -0,0 +1,25 @@ +import collections + +Card = collections.namedtuple("Card", ["rank", "suit"]) + + +class FrenchDeck: + ranks = [str(n) for n in range(2, 11)] + list("JQKA") + suits = "spades diamonds clubs hearts".split() + + def __init__(self): + self._cards = [Card(rank, suit) for suit in self.suits for rank in self.ranks] + + def __len__(self): + return len(self._cards) + + def __getitem__(self, position): + return self._cards[position] + + +suit_values = dict(spades=3, hearts=2, diamonds=1, clubs=0) + + +def spades_high(card): + rank_value = FrenchDeck.ranks.index(card.rank) + return rank_value * len(suit_values) + suit_values[card.suit] diff --git a/python_sandbox/python_sandbox/fluent_python/python_data_model/tests/test_cards.py b/python_sandbox/python_sandbox/fluent_python/python_data_model/tests/test_cards.py new file mode 100644 index 00000000..ae8e4c3e --- /dev/null +++ b/python_sandbox/python_sandbox/fluent_python/python_data_model/tests/test_cards.py @@ -0,0 +1,45 @@ +from python_data_model.cards import Card, FrenchDeck, spades_high +from random import choice +import sys + + +def test_cards(): + print(f"sys.path: {sys.path}") + beer_card = Card("7", "diamonds") + assert beer_card.rank == "7" + assert beer_card.suit == "diamonds" + + +def test_deck(): + deck = FrenchDeck() + assert len(deck) == 52 + assert deck[0] == Card("2", "spades") + assert deck[-1] == Card("A", "hearts") + # choice works with a sequence and FrenchDeck is a sequence because it implements + # both __len__ and __getitem__ + print(f"Pick a random card: {choice(deck)}") + print(f"Pick a random card: {choice(deck)}") + + # slicing works + assert deck[:3] == [Card("2", "spades"), Card("3", "spades"), Card("4", "spades")] + assert deck[-3:] == [Card("Q", "hearts"), Card("K", "hearts"), Card("A", "hearts")] + + # iteration works too because of __getitem__ + print("iterating cards") + for card in deck: + print(card) + + # in works too + # If a collection has no __contains__ method, the "in" operator does a sequential scan + assert Card("Q", "hearts") in deck + assert Card("7", "beasts") not in deck + + # sort cards + print("sorted cards") + for card in sorted(deck, key=spades_high): + print(card) + + # you can iterate the cards in reverse order + print("reversed cards") + for card in reversed(deck): + print(card) \ No newline at end of file diff --git a/python_sandbox/python_sandbox/fluent_python/python_data_model/tests/test_vectors.py b/python_sandbox/python_sandbox/fluent_python/python_data_model/tests/test_vectors.py new file mode 100644 index 00000000..29f222ec --- /dev/null +++ b/python_sandbox/python_sandbox/fluent_python/python_data_model/tests/test_vectors.py @@ -0,0 +1,13 @@ +from python_data_model.vectors import Vector + + +def test_vectors(): + v1 = Vector(2, 4) + v2 = Vector(2, 1) + assert v1 + v2 == Vector(4, 5) + + v = Vector(3, 4) + assert abs(v) == 5.0 + + print("v * 3:", v * 3) + assert v * 3 == Vector(9, 12) diff --git a/python_sandbox/python_sandbox/fluent_python/chapter1/vectors.py b/python_sandbox/python_sandbox/fluent_python/python_data_model/vectors.py similarity index 68% rename from python_sandbox/python_sandbox/fluent_python/chapter1/vectors.py rename to python_sandbox/python_sandbox/fluent_python/python_data_model/vectors.py index b3a1c10f..695ce7ea 100644 --- a/python_sandbox/python_sandbox/fluent_python/chapter1/vectors.py +++ b/python_sandbox/python_sandbox/fluent_python/python_data_model/vectors.py @@ -7,7 +7,7 @@ def __init__(self, x=0, y=0): self.y = y def __repr__(self): - return 'Vector(%r, %r)' % (self.x, self.y) + return "Vector(%r, %r)" % (self.x, self.y) def __abs__(self): return hypot(self.x, self.y) @@ -26,17 +26,5 @@ def __add__(self, other): def __mul__(self, scalar): return Vector(self.x * scalar, self.y * scalar) - -def main(): - v1 = Vector(2, 4) - v2 = Vector(2, 1) - print('v1 + v2:', v1 + v2) - - v = Vector(3, 4) - print('abs(v):', abs(v)) - - print('v * 3:', v * 3) - - -if __name__ == '__main__': - main() + def __eq__(self, other): + return self.x == other.x and self.y == other.y \ No newline at end of file diff --git a/python_sandbox/requirements.txt b/python_sandbox/requirements.txt index bc0bbaef..372d84ab 100644 --- a/python_sandbox/requirements.txt +++ b/python_sandbox/requirements.txt @@ -1,2 +1,3 @@ hydra-core==0.11.3 pyyaml==5.3.1 +pytest==7.1.3