move sections
[python/dscho.git] / Lib / test / test_descrtut.py
blob157b9f4fe9c25933875b9d72a46d3533698bd388
1 # This contains most of the executable examples from Guido's descr
2 # tutorial, once at
4 # http://www.python.org/2.2/descrintro.html
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.
11 from test.test_support import sortdict
12 import pprint
14 class defaultdict(dict):
15 def __init__(self, default=None):
16 dict.__init__(self)
17 self.default = default
19 def __getitem__(self, key):
20 try:
21 return dict.__getitem__(self, key)
22 except KeyError:
23 return self.default
25 def get(self, key, *args):
26 if not args:
27 args = (self.default,)
28 return dict.get(self, key, *args)
30 def merge(self, other):
31 for key in other:
32 if key not in self:
33 self[key] = other[key]
35 test_1 = """
37 Here's the new type at work:
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
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-existent 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 >>>
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():
68 >>> def sorted(seq):
69 ... seq.sort(key=str)
70 ... return seq
71 >>> print sorted(a.keys())
72 [1, 2]
73 >>> exec "x = 3; print x" in a
75 >>> print sorted(a.keys())
76 [1, 2, '__builtins__', 'x']
77 >>> print a['x']
79 >>>
81 Now I'll show that defaultdict instances have dynamic instance variables,
82 just like classic classes:
84 >>> a.default = -1
85 >>> print a["noway"]
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}
104 class defaultdict2(dict):
105 __slots__ = ['default']
107 def __init__(self, default=None):
108 dict.__init__(self)
109 self.default = default
111 def __getitem__(self, key):
112 try:
113 return dict.__getitem__(self, key)
114 except KeyError:
115 return self.default
117 def get(self, key, *args):
118 if not args:
119 args = (self.default,)
120 return dict.get(self, key, *args)
122 def merge(self, other):
123 for key in other:
124 if key not in self:
125 self[key] = other[key]
127 test_2 = """
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:
133 >>> a = defaultdict2(default=0.0)
134 >>> a[1]
136 >>> a.default = -1
137 >>> a[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'
147 test_3 = """
149 Introspecting instances of built-in types
151 For instance of built-in types, x.__class__ is now the same as type(x):
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
167 Under the new proposal, the __methods__ attribute no longer exists:
169 >>> [].__methods__
170 Traceback (most recent call last):
171 File "<stdin>", line 1, in ?
172 AttributeError: 'list' object has no attribute '__methods__'
175 Instead, you can get the same information from the list type:
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']
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:
229 >>> a = ['tic', 'tac']
230 >>> list.__len__(a) # same as len(a)
232 >>> a.__len__() # ditto
234 >>> list.append(a, 'toe') # same as a.append('toe')
235 >>> a
236 ['tic', 'tac', 'toe']
239 This is just like it is for user-defined classes.
242 test_4 = """
244 Static methods and class methods
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:
250 >>> class C:
252 ... @staticmethod
253 ... def foo(x, y):
254 ... print "staticmethod", x, y
256 >>> C.foo(1, 2)
257 staticmethod 1 2
258 >>> c = C()
259 >>> c.foo(1, 2)
260 staticmethod 1 2
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.
265 >>> class C:
266 ... @classmethod
267 ... def foo(cls, y):
268 ... print "classmethod", cls, y
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
276 >>> class D(C):
277 ... pass
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
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().
289 But notice this:
291 >>> class E(C):
292 ... @classmethod
293 ... def foo(cls, y): # override C.foo
294 ... print "E.foo() called"
295 ... C.foo(y)
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
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).
312 test_5 = """
314 Attributes defined by get/set methods
317 >>> class property(object):
319 ... def __init__(self, get, set=None):
320 ... self.__get = get
321 ... self.__set = set
323 ... def __get__(self, inst, type=None):
324 ... return self.__get(inst)
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)
331 Now let's define a class with an attribute x defined by a pair of methods,
332 getx() and and setx():
334 >>> class C(object):
336 ... def __init__(self):
337 ... self.__x = 0
339 ... def getx(self):
340 ... return self.__x
342 ... def setx(self, x):
343 ... if x < 0: x = 0
344 ... self.__x = x
346 ... x = property(getx, setx)
348 Here's a small demonstration:
350 >>> a = C()
351 >>> a.x = 10
352 >>> print a.x
354 >>> a.x = -10
355 >>> print a.x
359 Hmm -- property is builtin now, so let's try it that way too.
361 >>> del property # unmask the builtin
362 >>> property
363 <type 'property'>
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)
376 >>> a = C()
377 >>> a.x = 10
378 >>> print a.x
380 >>> a.x = -10
381 >>> print a.x
386 test_6 = """
388 Method resolution order
390 This example is implicit in the writeup.
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
403 >>> D().save()
404 called A.save()
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
417 >>> D().save()
418 called C.save()
421 class A(object):
422 def m(self):
423 return "A"
425 class B(A):
426 def m(self):
427 return "B" + super(B, self).m()
429 class C(A):
430 def m(self):
431 return "C" + super(C, self).m()
433 class D(C, B):
434 def m(self):
435 return "D" + super(D, self).m()
438 test_7 = """
440 Cooperative methods and "super"
442 >>> print D().m() # "DCBA"
443 DCBA
446 test_8 = """
448 Backwards incompatibilities
450 >>> class A:
451 ... def foo(self):
452 ... print "called A.foo()"
454 >>> class B(A):
455 ... pass
457 >>> class C(A):
458 ... def foo(self):
459 ... B.foo(self)
461 >>> C().foo()
462 Traceback (most recent call last):
464 TypeError: unbound method foo() must be called with B instance as first argument (got C instance instead)
466 >>> class C(A):
467 ... def foo(self):
468 ... A.foo(self)
469 >>> C().foo()
470 called A.foo()
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}
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)
495 # This part isn't needed for regrtest, but for running the test directly.
496 if __name__ == "__main__":
497 test_main(1)