1
+ [ Contents] ( ../Contents ) \| [ Previous (4.4 Exceptions)] ( ../04_Classes_objects/04_Defining_exceptions ) \| [ Next (5.2 Encapsulation)] ( 02_Classes_encapsulation )
2
+
1
3
# 5.1 Dictionaries Revisited
2
4
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.
5
7
6
8
### Dictionaries, Revisited
7
9
@@ -15,12 +17,14 @@ stock = {
15
17
}
16
18
```
17
19
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* .
20
23
21
24
### Dicts and Modules
22
25
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.
24
28
25
29
``` python
26
30
# foo.py
@@ -33,7 +37,7 @@ def spam():
33
37
...
34
38
```
35
39
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.
37
41
38
42
``` python
39
43
{
@@ -45,8 +49,9 @@ If we inspect `foo.__dict__` or `globals()`, you'll see the dictionary.
45
49
46
50
### Dicts and Objects
47
51
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.
50
55
51
56
A dictionary holds the instance data, ` __dict__ ` .
52
57
@@ -59,8 +64,8 @@ A dictionary holds the instance data, `__dict__`.
59
64
You populate this dict (and instance) when assigning to ` self ` .
60
65
61
66
``` python
62
- class Stock ( object ) :
63
- def __init__ (self ,name ,shares ,price ):
67
+ class Stock :
68
+ def __init__ (self , name , shares , price ):
64
69
self .name = name
65
70
self .shares = shares
66
71
self .price = price
@@ -79,27 +84,28 @@ The instance data, `self.__dict__`, looks like this:
79
84
** Each instance gets its own private dictionary.**
80
85
81
86
``` 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 }
84
89
```
85
90
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.
87
93
88
94
### Class Members
89
95
90
96
A separate dictionary also holds the methods.
91
97
92
98
``` python
93
- class Stock ( object ) :
94
- def __init__ (self ,name ,shares ,price ):
99
+ class Stock :
100
+ def __init__ (self , name , shares , price ):
95
101
self .name = name
96
102
self .shares = shares
97
103
self .price = price
98
104
99
105
def cost (self ):
100
106
return self .shares * self .price
101
107
102
- def sell (self ,nshares ):
108
+ def sell (self , nshares ):
103
109
self .shares -= nshares
104
110
```
105
111
@@ -115,8 +121,8 @@ The dictionary is in `Stock.__dict__`.
115
121
116
122
### Instances and Classes
117
123
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.
120
126
121
127
``` python
122
128
>> > s = Stock(' GOOG' , 100 , 490.1 )
@@ -127,7 +133,9 @@ The `__class__` attribute refers back to the class.
127
133
>> >
128
134
```
129
135
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.
131
139
132
140
### Attribute Access
133
141
@@ -207,26 +215,30 @@ This provides a link to parent classes.
207
215
208
216
### Reading Attributes with Inheritance
209
217
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.
212
222
213
223
### Reading Attributes with Single Inheritance
214
224
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.
216
227
217
228
``` python
218
- class A ( object ) : pass
229
+ class A : pass
219
230
class B (A ): pass
220
231
class C (A ): pass
221
232
class D (B ): pass
222
233
class E (D ): pass
223
234
```
224
- With Single Inheritance , there ia single path to the top.
235
+ With single inheritance , there is single path to the top.
225
236
You stop with the first match.
226
237
227
238
### Method Resolution Order or MRO
228
239
229
240
Python precomputes an inheritance chain and stores it in the * MRO* attribute on the class.
241
+ You can view it.
230
242
231
243
``` python
232
244
>> > E.__mro__
@@ -236,38 +248,39 @@ Python precomputes an inheritance chain and stores it in the *MRO* attribute on
236
248
>> >
237
249
```
238
250
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.
241
253
242
254
### MRO in Multiple Inheritance
243
255
244
- There is no single path to the top with multiple inheritance .
256
+ With multiple inheritance, there is no single path to the top.
245
257
Let's take a look at an example.
246
258
247
259
``` python
248
- class A ( object ) : pass
249
- class B ( object ) : pass
260
+ class A : pass
261
+ class B : pass
250
262
class C (A , B ): pass
251
263
class D (B ): pass
252
264
class E (C , D ): pass
253
265
```
254
266
255
- What happens when we do ?
267
+ What happens when you access at attribute ?
256
268
257
269
``` python
258
270
e = E()
259
271
e.attr
260
272
```
261
273
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.
263
275
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.
266
278
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.
269
281
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.
271
284
272
285
``` python
273
286
>> > E.__mro__
@@ -281,12 +294,18 @@ The MRO is computed using those rules.
281
294
>> >
282
295
```
283
296
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)
285
304
286
305
Consider two completely unrelated objects:
287
306
288
307
``` python
289
- class Dog ( object ) :
308
+ class Dog :
290
309
def noise (self ):
291
310
return ' Bark'
292
311
@@ -295,14 +314,14 @@ class Dog(object):
295
314
296
315
class LoudDog (Dog ):
297
316
def noise (self ):
298
- # Code commonality with LoudBike
317
+ # Code commonality with LoudBike (below)
299
318
return super ().noise().upper()
300
319
```
301
320
302
321
And
303
322
304
323
``` python
305
- class Bike ( object ) :
324
+ class Bike :
306
325
def noise (self ):
307
326
return ' On Your Left'
308
327
@@ -311,19 +330,20 @@ class Bike(object):
311
330
312
331
class LoudBike (Bike ):
313
332
def noise (self ):
314
- # Code commonality with LoudDog
333
+ # Code commonality with LoudDog (above)
315
334
return super ().noise().upper()
316
335
```
317
336
318
337
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.
320
340
321
341
### The "Mixin" Pattern
322
342
323
343
The * Mixin* pattern is a class with a fragment of code.
324
344
325
345
``` python
326
- class Loud ( object ) :
346
+ class Loud :
327
347
def noise (self ):
328
348
return super ().noise().upper()
329
349
```
@@ -339,26 +359,31 @@ class LoudBike(Loud, Bike):
339
359
pass
340
360
```
341
361
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.
343
365
344
366
### Why ` super() `
345
367
346
368
Always use ` super() ` when overriding methods.
347
369
348
370
``` python
349
- class Loud ( object ) :
371
+ class Loud :
350
372
def noise (self ):
351
373
return super ().noise().upper()
352
374
```
353
375
354
376
` super() ` delegates to the * next class* on the MRO.
355
377
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.
357
380
358
381
### Some Cautions
359
382
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.
362
387
363
388
## Exercises
364
389
@@ -376,7 +401,8 @@ few instances:
376
401
377
402
### Exercise 5.1: Representation of Instances
378
403
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:
380
406
381
407
``` python
382
408
>> > goog.__dict__
@@ -537,7 +563,7 @@ two steps and something known as a bound method. For example:
537
563
``` python
538
564
>> > s = goog.sell
539
565
>> > s
540
- < bound method Stock.sell of Stock(' GOOG' ,100 ,490.1 )>
566
+ < bound method Stock.sell of Stock(' GOOG' , 100 , 490.1 )>
541
567
>> > s(25 )
542
568
>> > goog.shares
543
569
75
0 commit comments