|
1 # This contains most of the executable examples from Guido's descr |
|
2 # tutorial, once at |
|
3 # |
|
4 # http://www.python.org/2.2/descrintro.html |
|
5 # |
|
6 # A few examples left implicit in the writeup were fleshed out, a few were |
|
7 # skipped due to lack of interest (e.g., faking super() by hand isn't |
|
8 # of much interest anymore), and a few were fiddled to make the output |
|
9 # deterministic. |
|
10 |
|
11 from test.test_support import sortdict |
|
12 import pprint |
|
13 |
|
14 class defaultdict(dict): |
|
15 def __init__(self, default=None): |
|
16 dict.__init__(self) |
|
17 self.default = default |
|
18 |
|
19 def __getitem__(self, key): |
|
20 try: |
|
21 return dict.__getitem__(self, key) |
|
22 except KeyError: |
|
23 return self.default |
|
24 |
|
25 def get(self, key, *args): |
|
26 if not args: |
|
27 args = (self.default,) |
|
28 return dict.get(self, key, *args) |
|
29 |
|
30 def merge(self, other): |
|
31 for key in other: |
|
32 if key not in self: |
|
33 self[key] = other[key] |
|
34 |
|
35 test_1 = """ |
|
36 |
|
37 Here's the new type at work: |
|
38 |
|
39 >>> print defaultdict # show our type |
|
40 <class 'test.test_descrtut.defaultdict'> |
|
41 >>> print type(defaultdict) # its metatype |
|
42 <type 'type'> |
|
43 >>> a = defaultdict(default=0.0) # create an instance |
|
44 >>> print a # show the instance |
|
45 {} |
|
46 >>> print type(a) # show its type |
|
47 <class 'test.test_descrtut.defaultdict'> |
|
48 >>> print a.__class__ # show its class |
|
49 <class 'test.test_descrtut.defaultdict'> |
|
50 >>> print type(a) is a.__class__ # its type is its class |
|
51 True |
|
52 >>> a[1] = 3.25 # modify the instance |
|
53 >>> print a # show the new value |
|
54 {1: 3.25} |
|
55 >>> print a[1] # show the new item |
|
56 3.25 |
|
57 >>> print a[0] # a non-existant item |
|
58 0.0 |
|
59 >>> a.merge({1:100, 2:200}) # use a dict method |
|
60 >>> print sortdict(a) # show the result |
|
61 {1: 3.25, 2: 200} |
|
62 >>> |
|
63 |
|
64 We can also use the new type in contexts where classic only allows "real" |
|
65 dictionaries, such as the locals/globals dictionaries for the exec |
|
66 statement or the built-in function eval(): |
|
67 |
|
68 >>> def sorted(seq): |
|
69 ... seq.sort() |
|
70 ... return seq |
|
71 >>> print sorted(a.keys()) |
|
72 [1, 2] |
|
73 >>> exec "x = 3; print x" in a |
|
74 3 |
|
75 >>> print sorted(a.keys()) |
|
76 [1, 2, '__builtins__', 'x'] |
|
77 >>> print a['x'] |
|
78 3 |
|
79 >>> |
|
80 |
|
81 Now I'll show that defaultdict instances have dynamic instance variables, |
|
82 just like classic classes: |
|
83 |
|
84 >>> a.default = -1 |
|
85 >>> print a["noway"] |
|
86 -1 |
|
87 >>> a.default = -1000 |
|
88 >>> print a["noway"] |
|
89 -1000 |
|
90 >>> 'default' in dir(a) |
|
91 True |
|
92 >>> a.x1 = 100 |
|
93 >>> a.x2 = 200 |
|
94 >>> print a.x1 |
|
95 100 |
|
96 >>> d = dir(a) |
|
97 >>> 'default' in d and 'x1' in d and 'x2' in d |
|
98 True |
|
99 >>> print sortdict(a.__dict__) |
|
100 {'default': -1000, 'x1': 100, 'x2': 200} |
|
101 >>> |
|
102 """ |
|
103 |
|
104 class defaultdict2(dict): |
|
105 __slots__ = ['default'] |
|
106 |
|
107 def __init__(self, default=None): |
|
108 dict.__init__(self) |
|
109 self.default = default |
|
110 |
|
111 def __getitem__(self, key): |
|
112 try: |
|
113 return dict.__getitem__(self, key) |
|
114 except KeyError: |
|
115 return self.default |
|
116 |
|
117 def get(self, key, *args): |
|
118 if not args: |
|
119 args = (self.default,) |
|
120 return dict.get(self, key, *args) |
|
121 |
|
122 def merge(self, other): |
|
123 for key in other: |
|
124 if key not in self: |
|
125 self[key] = other[key] |
|
126 |
|
127 test_2 = """ |
|
128 |
|
129 The __slots__ declaration takes a list of instance variables, and reserves |
|
130 space for exactly these in the instance. When __slots__ is used, other |
|
131 instance variables cannot be assigned to: |
|
132 |
|
133 >>> a = defaultdict2(default=0.0) |
|
134 >>> a[1] |
|
135 0.0 |
|
136 >>> a.default = -1 |
|
137 >>> a[1] |
|
138 -1 |
|
139 >>> a.x1 = 1 |
|
140 Traceback (most recent call last): |
|
141 File "<stdin>", line 1, in ? |
|
142 AttributeError: 'defaultdict2' object has no attribute 'x1' |
|
143 >>> |
|
144 |
|
145 """ |
|
146 |
|
147 test_3 = """ |
|
148 |
|
149 Introspecting instances of built-in types |
|
150 |
|
151 For instance of built-in types, x.__class__ is now the same as type(x): |
|
152 |
|
153 >>> type([]) |
|
154 <type 'list'> |
|
155 >>> [].__class__ |
|
156 <type 'list'> |
|
157 >>> list |
|
158 <type 'list'> |
|
159 >>> isinstance([], list) |
|
160 True |
|
161 >>> isinstance([], dict) |
|
162 False |
|
163 >>> isinstance([], object) |
|
164 True |
|
165 >>> |
|
166 |
|
167 Under the new proposal, the __methods__ attribute no longer exists: |
|
168 |
|
169 >>> [].__methods__ |
|
170 Traceback (most recent call last): |
|
171 File "<stdin>", line 1, in ? |
|
172 AttributeError: 'list' object has no attribute '__methods__' |
|
173 >>> |
|
174 |
|
175 Instead, you can get the same information from the list type: |
|
176 |
|
177 >>> pprint.pprint(dir(list)) # like list.__dict__.keys(), but sorted |
|
178 ['__add__', |
|
179 '__class__', |
|
180 '__contains__', |
|
181 '__delattr__', |
|
182 '__delitem__', |
|
183 '__delslice__', |
|
184 '__doc__', |
|
185 '__eq__', |
|
186 '__format__', |
|
187 '__ge__', |
|
188 '__getattribute__', |
|
189 '__getitem__', |
|
190 '__getslice__', |
|
191 '__gt__', |
|
192 '__hash__', |
|
193 '__iadd__', |
|
194 '__imul__', |
|
195 '__init__', |
|
196 '__iter__', |
|
197 '__le__', |
|
198 '__len__', |
|
199 '__lt__', |
|
200 '__mul__', |
|
201 '__ne__', |
|
202 '__new__', |
|
203 '__reduce__', |
|
204 '__reduce_ex__', |
|
205 '__repr__', |
|
206 '__reversed__', |
|
207 '__rmul__', |
|
208 '__setattr__', |
|
209 '__setitem__', |
|
210 '__setslice__', |
|
211 '__sizeof__', |
|
212 '__str__', |
|
213 '__subclasshook__', |
|
214 'append', |
|
215 'count', |
|
216 'extend', |
|
217 'index', |
|
218 'insert', |
|
219 'pop', |
|
220 'remove', |
|
221 'reverse', |
|
222 'sort'] |
|
223 |
|
224 The new introspection API gives more information than the old one: in |
|
225 addition to the regular methods, it also shows the methods that are |
|
226 normally invoked through special notations, e.g. __iadd__ (+=), __len__ |
|
227 (len), __ne__ (!=). You can invoke any method from this list directly: |
|
228 |
|
229 >>> a = ['tic', 'tac'] |
|
230 >>> list.__len__(a) # same as len(a) |
|
231 2 |
|
232 >>> a.__len__() # ditto |
|
233 2 |
|
234 >>> list.append(a, 'toe') # same as a.append('toe') |
|
235 >>> a |
|
236 ['tic', 'tac', 'toe'] |
|
237 >>> |
|
238 |
|
239 This is just like it is for user-defined classes. |
|
240 """ |
|
241 |
|
242 test_4 = """ |
|
243 |
|
244 Static methods and class methods |
|
245 |
|
246 The new introspection API makes it possible to add static methods and class |
|
247 methods. Static methods are easy to describe: they behave pretty much like |
|
248 static methods in C++ or Java. Here's an example: |
|
249 |
|
250 >>> class C: |
|
251 ... |
|
252 ... @staticmethod |
|
253 ... def foo(x, y): |
|
254 ... print "staticmethod", x, y |
|
255 |
|
256 >>> C.foo(1, 2) |
|
257 staticmethod 1 2 |
|
258 >>> c = C() |
|
259 >>> c.foo(1, 2) |
|
260 staticmethod 1 2 |
|
261 |
|
262 Class methods use a similar pattern to declare methods that receive an |
|
263 implicit first argument that is the *class* for which they are invoked. |
|
264 |
|
265 >>> class C: |
|
266 ... @classmethod |
|
267 ... def foo(cls, y): |
|
268 ... print "classmethod", cls, y |
|
269 |
|
270 >>> C.foo(1) |
|
271 classmethod test.test_descrtut.C 1 |
|
272 >>> c = C() |
|
273 >>> c.foo(1) |
|
274 classmethod test.test_descrtut.C 1 |
|
275 |
|
276 >>> class D(C): |
|
277 ... pass |
|
278 |
|
279 >>> D.foo(1) |
|
280 classmethod test.test_descrtut.D 1 |
|
281 >>> d = D() |
|
282 >>> d.foo(1) |
|
283 classmethod test.test_descrtut.D 1 |
|
284 |
|
285 This prints "classmethod __main__.D 1" both times; in other words, the |
|
286 class passed as the first argument of foo() is the class involved in the |
|
287 call, not the class involved in the definition of foo(). |
|
288 |
|
289 But notice this: |
|
290 |
|
291 >>> class E(C): |
|
292 ... @classmethod |
|
293 ... def foo(cls, y): # override C.foo |
|
294 ... print "E.foo() called" |
|
295 ... C.foo(y) |
|
296 |
|
297 >>> E.foo(1) |
|
298 E.foo() called |
|
299 classmethod test.test_descrtut.C 1 |
|
300 >>> e = E() |
|
301 >>> e.foo(1) |
|
302 E.foo() called |
|
303 classmethod test.test_descrtut.C 1 |
|
304 |
|
305 In this example, the call to C.foo() from E.foo() will see class C as its |
|
306 first argument, not class E. This is to be expected, since the call |
|
307 specifies the class C. But it stresses the difference between these class |
|
308 methods and methods defined in metaclasses (where an upcall to a metamethod |
|
309 would pass the target class as an explicit first argument). |
|
310 """ |
|
311 |
|
312 test_5 = """ |
|
313 |
|
314 Attributes defined by get/set methods |
|
315 |
|
316 |
|
317 >>> class property(object): |
|
318 ... |
|
319 ... def __init__(self, get, set=None): |
|
320 ... self.__get = get |
|
321 ... self.__set = set |
|
322 ... |
|
323 ... def __get__(self, inst, type=None): |
|
324 ... return self.__get(inst) |
|
325 ... |
|
326 ... def __set__(self, inst, value): |
|
327 ... if self.__set is None: |
|
328 ... raise AttributeError, "this attribute is read-only" |
|
329 ... return self.__set(inst, value) |
|
330 |
|
331 Now let's define a class with an attribute x defined by a pair of methods, |
|
332 getx() and and setx(): |
|
333 |
|
334 >>> class C(object): |
|
335 ... |
|
336 ... def __init__(self): |
|
337 ... self.__x = 0 |
|
338 ... |
|
339 ... def getx(self): |
|
340 ... return self.__x |
|
341 ... |
|
342 ... def setx(self, x): |
|
343 ... if x < 0: x = 0 |
|
344 ... self.__x = x |
|
345 ... |
|
346 ... x = property(getx, setx) |
|
347 |
|
348 Here's a small demonstration: |
|
349 |
|
350 >>> a = C() |
|
351 >>> a.x = 10 |
|
352 >>> print a.x |
|
353 10 |
|
354 >>> a.x = -10 |
|
355 >>> print a.x |
|
356 0 |
|
357 >>> |
|
358 |
|
359 Hmm -- property is builtin now, so let's try it that way too. |
|
360 |
|
361 >>> del property # unmask the builtin |
|
362 >>> property |
|
363 <type 'property'> |
|
364 |
|
365 >>> class C(object): |
|
366 ... def __init__(self): |
|
367 ... self.__x = 0 |
|
368 ... def getx(self): |
|
369 ... return self.__x |
|
370 ... def setx(self, x): |
|
371 ... if x < 0: x = 0 |
|
372 ... self.__x = x |
|
373 ... x = property(getx, setx) |
|
374 |
|
375 |
|
376 >>> a = C() |
|
377 >>> a.x = 10 |
|
378 >>> print a.x |
|
379 10 |
|
380 >>> a.x = -10 |
|
381 >>> print a.x |
|
382 0 |
|
383 >>> |
|
384 """ |
|
385 |
|
386 test_6 = """ |
|
387 |
|
388 Method resolution order |
|
389 |
|
390 This example is implicit in the writeup. |
|
391 |
|
392 >>> class A: # classic class |
|
393 ... def save(self): |
|
394 ... print "called A.save()" |
|
395 >>> class B(A): |
|
396 ... pass |
|
397 >>> class C(A): |
|
398 ... def save(self): |
|
399 ... print "called C.save()" |
|
400 >>> class D(B, C): |
|
401 ... pass |
|
402 |
|
403 >>> D().save() |
|
404 called A.save() |
|
405 |
|
406 >>> class A(object): # new class |
|
407 ... def save(self): |
|
408 ... print "called A.save()" |
|
409 >>> class B(A): |
|
410 ... pass |
|
411 >>> class C(A): |
|
412 ... def save(self): |
|
413 ... print "called C.save()" |
|
414 >>> class D(B, C): |
|
415 ... pass |
|
416 |
|
417 >>> D().save() |
|
418 called C.save() |
|
419 """ |
|
420 |
|
421 class A(object): |
|
422 def m(self): |
|
423 return "A" |
|
424 |
|
425 class B(A): |
|
426 def m(self): |
|
427 return "B" + super(B, self).m() |
|
428 |
|
429 class C(A): |
|
430 def m(self): |
|
431 return "C" + super(C, self).m() |
|
432 |
|
433 class D(C, B): |
|
434 def m(self): |
|
435 return "D" + super(D, self).m() |
|
436 |
|
437 |
|
438 test_7 = """ |
|
439 |
|
440 Cooperative methods and "super" |
|
441 |
|
442 >>> print D().m() # "DCBA" |
|
443 DCBA |
|
444 """ |
|
445 |
|
446 test_8 = """ |
|
447 |
|
448 Backwards incompatibilities |
|
449 |
|
450 >>> class A: |
|
451 ... def foo(self): |
|
452 ... print "called A.foo()" |
|
453 |
|
454 >>> class B(A): |
|
455 ... pass |
|
456 |
|
457 >>> class C(A): |
|
458 ... def foo(self): |
|
459 ... B.foo(self) |
|
460 |
|
461 >>> C().foo() |
|
462 Traceback (most recent call last): |
|
463 ... |
|
464 TypeError: unbound method foo() must be called with B instance as first argument (got C instance instead) |
|
465 |
|
466 >>> class C(A): |
|
467 ... def foo(self): |
|
468 ... A.foo(self) |
|
469 >>> C().foo() |
|
470 called A.foo() |
|
471 """ |
|
472 |
|
473 __test__ = {"tut1": test_1, |
|
474 "tut2": test_2, |
|
475 "tut3": test_3, |
|
476 "tut4": test_4, |
|
477 "tut5": test_5, |
|
478 "tut6": test_6, |
|
479 "tut7": test_7, |
|
480 "tut8": test_8} |
|
481 |
|
482 # Magic test name that regrtest.py invokes *after* importing this module. |
|
483 # This worms around a bootstrap problem. |
|
484 # Note that doctest and regrtest both look in sys.argv for a "-v" argument, |
|
485 # so this works as expected in both ways of running regrtest. |
|
486 def test_main(verbose=None): |
|
487 # Obscure: import this module as test.test_descrtut instead of as |
|
488 # plain test_descrtut because the name of this module works its way |
|
489 # into the doctest examples, and unless the full test.test_descrtut |
|
490 # business is used the name can change depending on how the test is |
|
491 # invoked. |
|
492 from test import test_support, test_descrtut |
|
493 test_support.run_doctest(test_descrtut, verbose) |
|
494 |
|
495 # This part isn't needed for regrtest, but for running the test directly. |
|
496 if __name__ == "__main__": |
|
497 test_main(1) |