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