Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 9572f70

Browse filesBrowse files
committed
More editing
1 parent cc15724 commit 9572f70
Copy full SHA for 9572f70

File tree

Expand file treeCollapse file tree

6 files changed

+160
-112
lines changed
Open diff view settings
Filter options
Expand file treeCollapse file tree

6 files changed

+160
-112
lines changed
Open diff view settings
Collapse file

‎Notes/05_Object_model/01_Dicts_revisited.md‎

Copy file name to clipboardExpand all lines: Notes/05_Object_model/01_Dicts_revisited.md
+76-50Lines changed: 76 additions & 50 deletions
  • Display the source diff
  • Display the rich diff
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 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.