Skip to content

Commit 9572f70

Browse files
committed
More editing
1 parent cc15724 commit 9572f70

6 files changed

+160
-112
lines changed

Diff for: Notes/05_Object_model/01_Dicts_revisited.md

+76-50
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1+
[Contents](../Contents) \| [Previous (4.4 Exceptions)](../04_Classes_objects/04_Defining_exceptions) \| [Next (5.2 Encapsulation)](02_Classes_encapsulation)
2+
13
# 5.1 Dictionaries Revisited
24

3-
The Python object system is largely based on an implementation based on dictionaries. This
4-
section discusses that.
5+
The Python object system is largely based on an implementation
6+
involving dictionaries. This section discusses that.
57

68
### Dictionaries, Revisited
79

@@ -15,12 +17,14 @@ stock = {
1517
}
1618
```
1719

18-
Dictionaries are commonly used for simple data structures.
19-
However, they are used for critical parts of the interpreter and may be the *most important type of data in Python*.
20+
Dictionaries are commonly used for simple data structures. However,
21+
they are used for critical parts of the interpreter and may be the
22+
*most important type of data in Python*.
2023

2124
### Dicts and Modules
2225

23-
In a module, a dictionary holds all of the global variables and functions.
26+
Within a module, a dictionary holds all of the global variables and
27+
functions.
2428

2529
```python
2630
# foo.py
@@ -33,7 +37,7 @@ def spam():
3337
...
3438
```
3539

36-
If we inspect `foo.__dict__` or `globals()`, you'll see the dictionary.
40+
If you inspect `foo.__dict__` or `globals()`, you'll see the dictionary.
3741

3842
```python
3943
{
@@ -45,8 +49,9 @@ If we inspect `foo.__dict__` or `globals()`, you'll see the dictionary.
4549

4650
### Dicts and Objects
4751

48-
User defined objects also use dictionaries for both instance data and classes.
49-
In fact, the entire object system is mostly an extra layer that's put on top of dictionaries.
52+
User defined objects also use dictionaries for both instance data and
53+
classes. In fact, the entire object system is mostly an extra layer
54+
that's put on top of dictionaries.
5055

5156
A dictionary holds the instance data, `__dict__`.
5257

@@ -59,8 +64,8 @@ A dictionary holds the instance data, `__dict__`.
5964
You populate this dict (and instance) when assigning to `self`.
6065

6166
```python
62-
class Stock(object):
63-
def __init__(self,name,shares,price):
67+
class Stock:
68+
def __init__(self, name, shares, price):
6469
self.name = name
6570
self.shares = shares
6671
self.price = price
@@ -79,27 +84,28 @@ The instance data, `self.__dict__`, looks like this:
7984
**Each instance gets its own private dictionary.**
8085

8186
```python
82-
s = Stock('GOOG',100,490.1) # {'name' : 'GOOG','shares' : 100, 'price': 490.1 }
83-
t = Stock('AAPL',50,123.45) # {'name' : 'AAPL','shares' : 50, 'price': 123.45 }
87+
s = Stock('GOOG', 100, 490.1) # {'name' : 'GOOG','shares' : 100, 'price': 490.1 }
88+
t = Stock('AAPL', 50, 123.45) # {'name' : 'AAPL','shares' : 50, 'price': 123.45 }
8489
```
8590

86-
If you created 200 instances of some class, there are 100 dictionaries sitting around holding data.
91+
If you created 100 instances of some class, there are 100 dictionaries
92+
sitting around holding data.
8793

8894
### Class Members
8995

9096
A separate dictionary also holds the methods.
9197

9298
```python
93-
class Stock(object):
94-
def __init__(self,name,shares,price):
99+
class Stock:
100+
def __init__(self, name, shares, price):
95101
self.name = name
96102
self.shares = shares
97103
self.price = price
98104

99105
def cost(self):
100106
return self.shares * self.price
101107

102-
def sell(self,nshares):
108+
def sell(self, nshares):
103109
self.shares -= nshares
104110
```
105111

@@ -115,8 +121,8 @@ The dictionary is in `Stock.__dict__`.
115121

116122
### Instances and Classes
117123

118-
Instances and classes are linked together.
119-
The `__class__` attribute refers back to the class.
124+
Instances and classes are linked together. The `__class__` attribute
125+
refers back to the class.
120126

121127
```python
122128
>>> s = Stock('GOOG', 100, 490.1)
@@ -127,7 +133,9 @@ The `__class__` attribute refers back to the class.
127133
>>>
128134
```
129135

130-
The instance dictionary holds data unique to each instance, whereas the class dictionary holds data collectively shared by *all* instances.
136+
The instance dictionary holds data unique to each instance, whereas
137+
the class dictionary holds data collectively shared by *all*
138+
instances.
131139

132140
### Attribute Access
133141

@@ -207,26 +215,30 @@ This provides a link to parent classes.
207215

208216
### Reading Attributes with Inheritance
209217

210-
First, check in local `__dict__`. If not found, look in `__dict__` of class through `__class__`.
211-
If not found in class, look in base classes through `__bases__`.
218+
Logically, the process of finding an attribute is as follows. First,
219+
check in local `__dict__`. If not found, look in `__dict__` of the
220+
class. If not found in class, look in the base classes through
221+
`__bases__`. However, there are some subtle aspects of this discussed next.
212222

213223
### Reading Attributes with Single Inheritance
214224

215-
In inheritance hierarchies, attributes are found by walking up the inheritance tree.
225+
In inheritance hierarchies, attributes are found by walking up the
226+
inheritance tree in order.
216227

217228
```python
218-
class A(object): pass
229+
class A: pass
219230
class B(A): pass
220231
class C(A): pass
221232
class D(B): pass
222233
class E(D): pass
223234
```
224-
With Single Inheritance, there ia single path to the top.
235+
With single inheritance, there is single path to the top.
225236
You stop with the first match.
226237

227238
### Method Resolution Order or MRO
228239

229240
Python precomputes an inheritance chain and stores it in the *MRO* attribute on the class.
241+
You can view it.
230242

231243
```python
232244
>>> E.__mro__
@@ -236,38 +248,39 @@ Python precomputes an inheritance chain and stores it in the *MRO* attribute on
236248
>>>
237249
```
238250

239-
This chain is called the **Method Resolutin Order**.
240-
The find the attributes, Python walks the MRO. First match, wins.
251+
This chain is called the **Method Resolutin Order**. The find an
252+
attribute, Python walks the MRO in order. The first match wins.
241253

242254
### MRO in Multiple Inheritance
243255

244-
There is no single path to the top with multiple inheritance.
256+
With multiple inheritance, there is no single path to the top.
245257
Let's take a look at an example.
246258

247259
```python
248-
class A(object): pass
249-
class B(object): pass
260+
class A: pass
261+
class B: pass
250262
class C(A, B): pass
251263
class D(B): pass
252264
class E(C, D): pass
253265
```
254266

255-
What happens when we do?
267+
What happens when you access at attribute?
256268

257269
```python
258270
e = E()
259271
e.attr
260272
```
261273

262-
A similar search process is carried out, but what is the order? That's a problem.
274+
A attribute search process is carried out, but what is the order? That's a problem.
263275

264-
Python uses *cooperative multiple inheritance*.
265-
These are some rules about class ordering:
276+
Python uses *cooperative multiple inheritance* which obeys some rules
277+
about class ordering.
266278

267-
* Children before parents
268-
* Parents go in order
279+
* Children are always checked before parents
280+
* Parents (if multiple) are always checked in the order listed.
269281

270-
The MRO is computed using those rules.
282+
The MRO is computed by sorting all of the classes in a hierarchy
283+
according to those rules.
271284

272285
```python
273286
>>> E.__mro__
@@ -281,12 +294,18 @@ The MRO is computed using those rules.
281294
>>>
282295
```
283296

284-
### An Odd Code Reuse
297+
The underlying algorithm is called the "C3 Linearization Algorithm."
298+
The precise details aren't important as long as you remember that a
299+
class hierarchy obeys the same ordering rules you might follow if your
300+
house was on fire and you had to evacuate--children first, followed by
301+
parents.
302+
303+
### An Odd Code Reuse (Involving Multiple Inheritance)
285304

286305
Consider two completely unrelated objects:
287306

288307
```python
289-
class Dog(object):
308+
class Dog:
290309
def noise(self):
291310
return 'Bark'
292311

@@ -295,14 +314,14 @@ class Dog(object):
295314

296315
class LoudDog(Dog):
297316
def noise(self):
298-
# Code commonality with LoudBike
317+
# Code commonality with LoudBike (below)
299318
return super().noise().upper()
300319
```
301320

302321
And
303322

304323
```python
305-
class Bike(object):
324+
class Bike:
306325
def noise(self):
307326
return 'On Your Left'
308327

@@ -311,19 +330,20 @@ class Bike(object):
311330

312331
class LoudBike(Bike):
313332
def noise(self):
314-
# Code commonality with LoudDog
333+
# Code commonality with LoudDog (above)
315334
return super().noise().upper()
316335
```
317336

318337
There is a code commonality in the implementation of `LoudDog.noise()` and
319-
`LoudBike.noise()`. In fact, the code is exactly the same.
338+
`LoudBike.noise()`. In fact, the code is exactly the same. Naturally,
339+
code like that is bound to attract software engineers.
320340

321341
### The "Mixin" Pattern
322342

323343
The *Mixin* pattern is a class with a fragment of code.
324344

325345
```python
326-
class Loud(object):
346+
class Loud:
327347
def noise(self):
328348
return super().noise().upper()
329349
```
@@ -339,26 +359,31 @@ class LoudBike(Loud, Bike):
339359
pass
340360
```
341361

342-
This is one of the primary uses of multiple inheritance in Python.
362+
Miraculously, loudness was now implemented just once and reused
363+
in two completely unrelated classes. This sort of trick is one
364+
of the primary uses of multiple inheritance in Python.
343365

344366
### Why `super()`
345367

346368
Always use `super()` when overriding methods.
347369

348370
```python
349-
class Loud(object):
371+
class Loud:
350372
def noise(self):
351373
return super().noise().upper()
352374
```
353375

354376
`super()` delegates to the *next class* on the MRO.
355377

356-
The tricky bit is that you don't know what it is when you create the Mixin.
378+
The tricky bit is that you don't know what it is. You especially don't
379+
know what it is if multiple inheritance is being used.
357380

358381
### Some Cautions
359382

360-
Multiple inheritance is a powerful tool. Remember that with power comes responsibility.
361-
Frameworks / libraries sometimes use it for advanced features involving composition of components.
383+
Multiple inheritance is a powerful tool. Remember that with power
384+
comes responsibility. Frameworks / libraries sometimes use it for
385+
advanced features involving composition of components. Now, forget
386+
that you saw that.
362387

363388
## Exercises
364389

@@ -376,7 +401,8 @@ few instances:
376401

377402
### Exercise 5.1: Representation of Instances
378403

379-
At the interactive shell, inspect the underlying dictionaries of the two instances you created:
404+
At the interactive shell, inspect the underlying dictionaries of the
405+
two instances you created:
380406

381407
```python
382408
>>> goog.__dict__
@@ -537,7 +563,7 @@ two steps and something known as a bound method. For example:
537563
```python
538564
>>> s = goog.sell
539565
>>> s
540-
<bound method Stock.sell of Stock('GOOG',100,490.1)>
566+
<bound method Stock.sell of Stock('GOOG', 100, 490.1)>
541567
>>> s(25)
542568
>>> goog.shares
543569
75

0 commit comments

Comments
 (0)