• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
11from test.test_support import sortdict
12import pprint
13
14class 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
35test_1 = """
36
37Here'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-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    >>>
63
64We can also use the new type in contexts where classic only allows "real"
65dictionaries, such as the locals/globals dictionaries for the exec
66statement or the built-in function eval():
67
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
74    3
75    >>> print sorted(a.keys())
76    [1, 2, '__builtins__', 'x']
77    >>> print a['x']
78    3
79    >>>
80
81Now I'll show that defaultdict instances have dynamic instance variables,
82just 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
104class 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
127test_2 = """
128
129The __slots__ declaration takes a list of instance variables, and reserves
130space for exactly these in the instance. When __slots__ is used, other
131instance 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
147test_3 = """
148
149Introspecting instances of built-in types
150
151For 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
167Under 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
175Instead, 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
224The new introspection API gives more information than the old one:  in
225addition to the regular methods, it also shows the methods that are
226normally 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
239This is just like it is for user-defined classes.
240"""
241
242test_4 = """
243
244Static methods and class methods
245
246The new introspection API makes it possible to add static methods and class
247methods. Static methods are easy to describe: they behave pretty much like
248static 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
262Class methods use a similar pattern to declare methods that receive an
263implicit 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
285This prints "classmethod __main__.D 1" both times; in other words, the
286class passed as the first argument of foo() is the class involved in the
287call, not the class involved in the definition of foo().
288
289But 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
305In this example, the call to C.foo() from E.foo() will see class C as its
306first argument, not class E. This is to be expected, since the call
307specifies the class C. But it stresses the difference between these class
308methods and methods defined in metaclasses (where an upcall to a metamethod
309would pass the target class as an explicit first argument).
310"""
311
312test_5 = """
313
314Attributes 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
331Now let's define a class with an attribute x defined by a pair of methods,
332getx() 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
348Here'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
359Hmm -- 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
386test_6 = """
387
388Method resolution order
389
390This 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()
404called 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()
418called C.save()
419"""
420
421class A(object):
422    def m(self):
423        return "A"
424
425class B(A):
426    def m(self):
427        return "B" + super(B, self).m()
428
429class C(A):
430    def m(self):
431        return "C" + super(C, self).m()
432
433class D(C, B):
434    def m(self):
435        return "D" + super(D, self).m()
436
437
438test_7 = """
439
440Cooperative methods and "super"
441
442>>> print D().m() # "DCBA"
443DCBA
444"""
445
446test_8 = """
447
448Backwards 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()
462Traceback (most recent call last):
463 ...
464TypeError: 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()
470called 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.
486def 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.
496if __name__ == "__main__":
497    test_main(1)
498