Fluent Python - Chapter 1. The Python Data Model
You can think of the data model as a description of Python as a framework. It formalizes the interfaces of the building blocks of the language itself, such as sequences, iterators, functions, classes, context managers, and so on.
A Pythonic Card Deck
Special methods: __getitem__
and __len__
.
1 | import collections |
nametuple
can be used to build classes of objects that are just bundles of attributes with no custom methods.
1 | beer_card = Card('7', 'diamonds') |
len()
function returns the number of cards in it.
1 | deck = FrenchDeck() |
index operation, provided by __getitem__
method.
1 | deck[0] |
Card(rank='2', suit='spades')
1 | deck[-1] |
Card(rank='A', suit='hearts')
Get a random item from a sequence
1 | from random import choice |
1 | choice(deck) |
Card(rank='9', suit='hearts')
Slicing support, because __getitem__
delegates to the [] oeprator of self._cards
1 | deck[:3] |
[Card(rank='2', suit='spades'), Card(rank='3', suit='spades'), Card(rank='4', suit='spades')]
1 | deck[12::13] |
[Card(rank='A', suit='spades'), Card(rank='A', suit='diamonds'), Card(rank='A', suit='clubs'), Card(rank='A', suit='hearts')]
__getitem__
method make the deck iterable
1 | for card in deck: |
1 | Card(rank='2', suit='spades') |
Iterated in reverse
1 | for card in reversed(deck): |
1 | Card(rank='A', suit='hearts') |
If a collection has no __contains__
, the in
oeprator does a sequential scan.
1 | Card('Q', 'hearts') in deck |
True
1 | Card('7', 'beasts') in deck |
False
Sorting
1 | suit_values = dict(spades=3, hearts=2, diamonds=1, clubs=0) |
Card(rank='2', suit='clubs')
Card(rank='2', suit='diamonds')
Card(rank='2', suit='hearts')
...
How Special Methods Are Used
Special methods are meant to be called by Python intepreter, and not by you. You don’t write my_object.__len__()
. You write len(my_object) and, then Python calls the __len__
instance method you implemented.
i in x:
invocates iter(x)
, which in turn may call x.__iter()
if that is available.
It is usually bette to call the relted built-in function, len, iter, str, etc. These built-ins call the corresponding special method.
Emulatin Numeric Types
1 | from math import hypot |
1 | v1 = Vector(4, 1) |
Vector(7, 5)
1 | v = Vector(3, 4) |
5.0
1 | v * 3 |
Vector(9, 12)
1 | abs(v * 3) |
15.0
1 | bool(v) |
True
String Representation
Use %r to obtain the standard representation
str()
will call __repr__
as a fallback, if __str__
is not available.
__repr
is to be unambiguous.
__str__
is to be readable.
Arithmetic Operators
__add__
and __mul__
return new instance, not touching either operand.
Boolean Value of a Custom Type
Here we return the magnitude of the vector.
bool(x)
calls x.__bool__()
. If x.__bool__()
is not implemented, call x.__len__()
, zero returns False. Otherwise bool returns True.
Overview of Special Methods
Table 1-1. Special method names (operators excluded)
Category | Method names |
---|---|
String/bytes representation | __repr__, str, format, bytes |
Conversion to number | __abs__, bool, complex, init_, float, hash, index |
Emulating collections | __len__, getitem, setitem__, delitem, contains, iter, reversed next |
Iteration | __iter__, reversed, next |
Emulating callables | __call__ |
Context management | __enter__, exit |
Instance creation and destruction | __new__, init, del |
Attribute management | __getattr__, getattribute__, setattr, delattr, dir |
Attribute descriptors | __get__, set, delete |
Class services | __prepare__, instancecheck, subclasscheck |
Table 1-2. Special method names for operators
Why len Is Not a Method
“The Zen of Python”: “Practicality beats purity”
len(x)
reads from a filed in a C struct of CPython, when x is a built-in type.
You can still customize it through __len__
.