1 # This contains most of the executable examples from Guido's descr
2 # tutorial, once at
3 #
4 # https://www.python.org/download/releases/2.2.3/descrintro/
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.support import sortdict
12 import doctest
13 import unittest
14
15
16 class ESC[4;38;5;81mdefaultdict(ESC[4;38;5;149mdict):
17 def __init__(self, default=None):
18 dict.__init__(self)
19 self.default = default
20
21 def __getitem__(self, key):
22 try:
23 return dict.__getitem__(self, key)
24 except KeyError:
25 return self.default
26
27 def get(self, key, *args):
28 if not args:
29 args = (self.default,)
30 return dict.get(self, key, *args)
31
32 def merge(self, other):
33 for key in other:
34 if key not in self:
35 self[key] = other[key]
36
37 test_1 = """
38
39 Here's the new type at work:
40
41 >>> print(defaultdict) # show our type
42 <class 'test.test_descrtut.defaultdict'>
43 >>> print(type(defaultdict)) # its metatype
44 <class 'type'>
45 >>> a = defaultdict(default=0.0) # create an instance
46 >>> print(a) # show the instance
47 {}
48 >>> print(type(a)) # show its type
49 <class 'test.test_descrtut.defaultdict'>
50 >>> print(a.__class__) # show its class
51 <class 'test.test_descrtut.defaultdict'>
52 >>> print(type(a) is a.__class__) # its type is its class
53 True
54 >>> a[1] = 3.25 # modify the instance
55 >>> print(a) # show the new value
56 {1: 3.25}
57 >>> print(a[1]) # show the new item
58 3.25
59 >>> print(a[0]) # a non-existent item
60 0.0
61 >>> a.merge({1:100, 2:200}) # use a dict method
62 >>> print(sortdict(a)) # show the result
63 {1: 3.25, 2: 200}
64 >>>
65
66 We can also use the new type in contexts where classic only allows "real"
67 dictionaries, such as the locals/globals dictionaries for the exec
68 statement or the built-in function eval():
69
70 >>> print(sorted(a.keys()))
71 [1, 2]
72 >>> a['print'] = print # need the print function here
73 >>> exec("x = 3; print(x)", a)
74 3
75 >>> print(sorted(a.keys(), key=lambda x: (str(type(x)), x)))
76 [1, 2, '__builtins__', 'print', '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 ESC[4;38;5;81mdefaultdict2(ESC[4;38;5;149mdict):
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 <class 'list'>
155 >>> [].__class__
156 <class 'list'>
157 >>> list
158 <class 'list'>
159 >>> isinstance([], list)
160 True
161 >>> isinstance([], dict)
162 False
163 >>> isinstance([], object)
164 True
165 >>>
166
167 You can get the information from the list type:
168
169 >>> import pprint
170 >>> pprint.pprint(dir(list)) # like list.__dict__.keys(), but sorted
171 ['__add__',
172 '__class__',
173 '__class_getitem__',
174 '__contains__',
175 '__delattr__',
176 '__delitem__',
177 '__dir__',
178 '__doc__',
179 '__eq__',
180 '__format__',
181 '__ge__',
182 '__getattribute__',
183 '__getitem__',
184 '__getstate__',
185 '__gt__',
186 '__hash__',
187 '__iadd__',
188 '__imul__',
189 '__init__',
190 '__init_subclass__',
191 '__iter__',
192 '__le__',
193 '__len__',
194 '__lt__',
195 '__mul__',
196 '__ne__',
197 '__new__',
198 '__reduce__',
199 '__reduce_ex__',
200 '__repr__',
201 '__reversed__',
202 '__rmul__',
203 '__setattr__',
204 '__setitem__',
205 '__sizeof__',
206 '__str__',
207 '__subclasshook__',
208 'append',
209 'clear',
210 'copy',
211 'count',
212 'extend',
213 'index',
214 'insert',
215 'pop',
216 'remove',
217 'reverse',
218 'sort']
219
220 The new introspection API gives more information than the old one: in
221 addition to the regular methods, it also shows the methods that are
222 normally invoked through special notations, e.g. __iadd__ (+=), __len__
223 (len), __ne__ (!=). You can invoke any method from this list directly:
224
225 >>> a = ['tic', 'tac']
226 >>> list.__len__(a) # same as len(a)
227 2
228 >>> a.__len__() # ditto
229 2
230 >>> list.append(a, 'toe') # same as a.append('toe')
231 >>> a
232 ['tic', 'tac', 'toe']
233 >>>
234
235 This is just like it is for user-defined classes.
236 """
237
238 test_4 = """
239
240 Static methods and class methods
241
242 The new introspection API makes it possible to add static methods and class
243 methods. Static methods are easy to describe: they behave pretty much like
244 static methods in C++ or Java. Here's an example:
245
246 >>> class C:
247 ...
248 ... @staticmethod
249 ... def foo(x, y):
250 ... print("staticmethod", x, y)
251
252 >>> C.foo(1, 2)
253 staticmethod 1 2
254 >>> c = C()
255 >>> c.foo(1, 2)
256 staticmethod 1 2
257
258 Class methods use a similar pattern to declare methods that receive an
259 implicit first argument that is the *class* for which they are invoked.
260
261 >>> class C:
262 ... @classmethod
263 ... def foo(cls, y):
264 ... print("classmethod", cls, y)
265
266 >>> C.foo(1)
267 classmethod <class 'test.test_descrtut.C'> 1
268 >>> c = C()
269 >>> c.foo(1)
270 classmethod <class 'test.test_descrtut.C'> 1
271
272 >>> class D(C):
273 ... pass
274
275 >>> D.foo(1)
276 classmethod <class 'test.test_descrtut.D'> 1
277 >>> d = D()
278 >>> d.foo(1)
279 classmethod <class 'test.test_descrtut.D'> 1
280
281 This prints "classmethod __main__.D 1" both times; in other words, the
282 class passed as the first argument of foo() is the class involved in the
283 call, not the class involved in the definition of foo().
284
285 But notice this:
286
287 >>> class E(C):
288 ... @classmethod
289 ... def foo(cls, y): # override C.foo
290 ... print("E.foo() called")
291 ... C.foo(y)
292
293 >>> E.foo(1)
294 E.foo() called
295 classmethod <class 'test.test_descrtut.C'> 1
296 >>> e = E()
297 >>> e.foo(1)
298 E.foo() called
299 classmethod <class 'test.test_descrtut.C'> 1
300
301 In this example, the call to C.foo() from E.foo() will see class C as its
302 first argument, not class E. This is to be expected, since the call
303 specifies the class C. But it stresses the difference between these class
304 methods and methods defined in metaclasses (where an upcall to a metamethod
305 would pass the target class as an explicit first argument).
306 """
307
308 test_5 = """
309
310 Attributes defined by get/set methods
311
312
313 >>> class property(object):
314 ...
315 ... def __init__(self, get, set=None):
316 ... self.__get = get
317 ... self.__set = set
318 ...
319 ... def __get__(self, inst, type=None):
320 ... return self.__get(inst)
321 ...
322 ... def __set__(self, inst, value):
323 ... if self.__set is None:
324 ... raise AttributeError("this attribute is read-only")
325 ... return self.__set(inst, value)
326
327 Now let's define a class with an attribute x defined by a pair of methods,
328 getx() and setx():
329
330 >>> class C(object):
331 ...
332 ... def __init__(self):
333 ... self.__x = 0
334 ...
335 ... def getx(self):
336 ... return self.__x
337 ...
338 ... def setx(self, x):
339 ... if x < 0: x = 0
340 ... self.__x = x
341 ...
342 ... x = property(getx, setx)
343
344 Here's a small demonstration:
345
346 >>> a = C()
347 >>> a.x = 10
348 >>> print(a.x)
349 10
350 >>> a.x = -10
351 >>> print(a.x)
352 0
353 >>>
354
355 Hmm -- property is builtin now, so let's try it that way too.
356
357 >>> del property # unmask the builtin
358 >>> property
359 <class 'property'>
360
361 >>> class C(object):
362 ... def __init__(self):
363 ... self.__x = 0
364 ... def getx(self):
365 ... return self.__x
366 ... def setx(self, x):
367 ... if x < 0: x = 0
368 ... self.__x = x
369 ... x = property(getx, setx)
370
371
372 >>> a = C()
373 >>> a.x = 10
374 >>> print(a.x)
375 10
376 >>> a.x = -10
377 >>> print(a.x)
378 0
379 >>>
380 """
381
382 test_6 = """
383
384 Method resolution order
385
386 This example is implicit in the writeup.
387
388 >>> class A: # implicit new-style class
389 ... def save(self):
390 ... print("called A.save()")
391 >>> class B(A):
392 ... pass
393 >>> class C(A):
394 ... def save(self):
395 ... print("called C.save()")
396 >>> class D(B, C):
397 ... pass
398
399 >>> D().save()
400 called C.save()
401
402 >>> class A(object): # explicit new-style class
403 ... def save(self):
404 ... print("called A.save()")
405 >>> class B(A):
406 ... pass
407 >>> class C(A):
408 ... def save(self):
409 ... print("called C.save()")
410 >>> class D(B, C):
411 ... pass
412
413 >>> D().save()
414 called C.save()
415 """
416
417 class ESC[4;38;5;81mA(ESC[4;38;5;149mobject):
418 def m(self):
419 return "A"
420
421 class ESC[4;38;5;81mB(ESC[4;38;5;149mA):
422 def m(self):
423 return "B" + super(B, self).m()
424
425 class ESC[4;38;5;81mC(ESC[4;38;5;149mA):
426 def m(self):
427 return "C" + super(C, self).m()
428
429 class ESC[4;38;5;81mD(ESC[4;38;5;149mC, ESC[4;38;5;149mB):
430 def m(self):
431 return "D" + super(D, self).m()
432
433
434 test_7 = """
435
436 Cooperative methods and "super"
437
438 >>> print(D().m()) # "DCBA"
439 DCBA
440 """
441
442 test_8 = """
443
444 Backwards incompatibilities
445
446 >>> class A:
447 ... def foo(self):
448 ... print("called A.foo()")
449
450 >>> class B(A):
451 ... pass
452
453 >>> class C(A):
454 ... def foo(self):
455 ... B.foo(self)
456
457 >>> C().foo()
458 called A.foo()
459
460 >>> class C(A):
461 ... def foo(self):
462 ... A.foo(self)
463 >>> C().foo()
464 called A.foo()
465 """
466
467 __test__ = {"tut1": test_1,
468 "tut2": test_2,
469 "tut3": test_3,
470 "tut4": test_4,
471 "tut5": test_5,
472 "tut6": test_6,
473 "tut7": test_7,
474 "tut8": test_8}
475
476 def load_tests(loader, tests, pattern):
477 tests.addTest(doctest.DocTestSuite())
478 return tests
479
480
481 if __name__ == "__main__":
482 unittest.main()