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     '__contains__',
171     '__delattr__',
172     '__delitem__',
173     '__dir__',
174     '__doc__',
175     '__eq__',
176     '__format__',
177     '__ge__',
178     '__getattribute__',
179     '__getitem__',
180     '__gt__',
181     '__hash__',
182     '__iadd__',
183     '__imul__',
184     '__init__',
185     '__init_subclass__',
186     '__iter__',
187     '__le__',
188     '__len__',
189     '__lt__',
190     '__mul__',
191     '__ne__',
192     '__new__',
193     '__reduce__',
194     '__reduce_ex__',
195     '__repr__',
196     '__reversed__',
197     '__rmul__',
198     '__setattr__',
199     '__setitem__',
200     '__sizeof__',
201     '__str__',
202     '__subclasshook__',
203     'append',
204     'clear',
205     'copy',
206     'count',
207     'extend',
208     'index',
209     'insert',
210     'pop',
211     'remove',
212     'reverse',
213     'sort']
214
215The new introspection API gives more information than the old one:  in
216addition to the regular methods, it also shows the methods that are
217normally invoked through special notations, e.g. __iadd__ (+=), __len__
218(len), __ne__ (!=). You can invoke any method from this list directly:
219
220    >>> a = ['tic', 'tac']
221    >>> list.__len__(a)          # same as len(a)
222    2
223    >>> a.__len__()              # ditto
224    2
225    >>> list.append(a, 'toe')    # same as a.append('toe')
226    >>> a
227    ['tic', 'tac', 'toe']
228    >>>
229
230This is just like it is for user-defined classes.
231"""
232
233test_4 = """
234
235Static methods and class methods
236
237The new introspection API makes it possible to add static methods and class
238methods. Static methods are easy to describe: they behave pretty much like
239static methods in C++ or Java. Here's an example:
240
241    >>> class C:
242    ...
243    ...     @staticmethod
244    ...     def foo(x, y):
245    ...         print("staticmethod", x, y)
246
247    >>> C.foo(1, 2)
248    staticmethod 1 2
249    >>> c = C()
250    >>> c.foo(1, 2)
251    staticmethod 1 2
252
253Class methods use a similar pattern to declare methods that receive an
254implicit first argument that is the *class* for which they are invoked.
255
256    >>> class C:
257    ...     @classmethod
258    ...     def foo(cls, y):
259    ...         print("classmethod", cls, y)
260
261    >>> C.foo(1)
262    classmethod <class 'test.test_descrtut.C'> 1
263    >>> c = C()
264    >>> c.foo(1)
265    classmethod <class 'test.test_descrtut.C'> 1
266
267    >>> class D(C):
268    ...     pass
269
270    >>> D.foo(1)
271    classmethod <class 'test.test_descrtut.D'> 1
272    >>> d = D()
273    >>> d.foo(1)
274    classmethod <class 'test.test_descrtut.D'> 1
275
276This prints "classmethod __main__.D 1" both times; in other words, the
277class passed as the first argument of foo() is the class involved in the
278call, not the class involved in the definition of foo().
279
280But notice this:
281
282    >>> class E(C):
283    ...     @classmethod
284    ...     def foo(cls, y): # override C.foo
285    ...         print("E.foo() called")
286    ...         C.foo(y)
287
288    >>> E.foo(1)
289    E.foo() called
290    classmethod <class 'test.test_descrtut.C'> 1
291    >>> e = E()
292    >>> e.foo(1)
293    E.foo() called
294    classmethod <class 'test.test_descrtut.C'> 1
295
296In this example, the call to C.foo() from E.foo() will see class C as its
297first argument, not class E. This is to be expected, since the call
298specifies the class C. But it stresses the difference between these class
299methods and methods defined in metaclasses (where an upcall to a metamethod
300would pass the target class as an explicit first argument).
301"""
302
303test_5 = """
304
305Attributes defined by get/set methods
306
307
308    >>> class property(object):
309    ...
310    ...     def __init__(self, get, set=None):
311    ...         self.__get = get
312    ...         self.__set = set
313    ...
314    ...     def __get__(self, inst, type=None):
315    ...         return self.__get(inst)
316    ...
317    ...     def __set__(self, inst, value):
318    ...         if self.__set is None:
319    ...             raise AttributeError("this attribute is read-only")
320    ...         return self.__set(inst, value)
321
322Now let's define a class with an attribute x defined by a pair of methods,
323getx() and setx():
324
325    >>> class C(object):
326    ...
327    ...     def __init__(self):
328    ...         self.__x = 0
329    ...
330    ...     def getx(self):
331    ...         return self.__x
332    ...
333    ...     def setx(self, x):
334    ...         if x < 0: x = 0
335    ...         self.__x = x
336    ...
337    ...     x = property(getx, setx)
338
339Here's a small demonstration:
340
341    >>> a = C()
342    >>> a.x = 10
343    >>> print(a.x)
344    10
345    >>> a.x = -10
346    >>> print(a.x)
347    0
348    >>>
349
350Hmm -- property is builtin now, so let's try it that way too.
351
352    >>> del property  # unmask the builtin
353    >>> property
354    <class 'property'>
355
356    >>> class C(object):
357    ...     def __init__(self):
358    ...         self.__x = 0
359    ...     def getx(self):
360    ...         return self.__x
361    ...     def setx(self, x):
362    ...         if x < 0: x = 0
363    ...         self.__x = x
364    ...     x = property(getx, setx)
365
366
367    >>> a = C()
368    >>> a.x = 10
369    >>> print(a.x)
370    10
371    >>> a.x = -10
372    >>> print(a.x)
373    0
374    >>>
375"""
376
377test_6 = """
378
379Method resolution order
380
381This example is implicit in the writeup.
382
383>>> class A:    # implicit new-style class
384...     def save(self):
385...         print("called A.save()")
386>>> class B(A):
387...     pass
388>>> class C(A):
389...     def save(self):
390...         print("called C.save()")
391>>> class D(B, C):
392...     pass
393
394>>> D().save()
395called C.save()
396
397>>> class A(object):  # explicit new-style class
398...     def save(self):
399...         print("called A.save()")
400>>> class B(A):
401...     pass
402>>> class C(A):
403...     def save(self):
404...         print("called C.save()")
405>>> class D(B, C):
406...     pass
407
408>>> D().save()
409called C.save()
410"""
411
412class A(object):
413    def m(self):
414        return "A"
415
416class B(A):
417    def m(self):
418        return "B" + super(B, self).m()
419
420class C(A):
421    def m(self):
422        return "C" + super(C, self).m()
423
424class D(C, B):
425    def m(self):
426        return "D" + super(D, self).m()
427
428
429test_7 = """
430
431Cooperative methods and "super"
432
433>>> print(D().m()) # "DCBA"
434DCBA
435"""
436
437test_8 = """
438
439Backwards incompatibilities
440
441>>> class A:
442...     def foo(self):
443...         print("called A.foo()")
444
445>>> class B(A):
446...     pass
447
448>>> class C(A):
449...     def foo(self):
450...         B.foo(self)
451
452>>> C().foo()
453called A.foo()
454
455>>> class C(A):
456...     def foo(self):
457...         A.foo(self)
458>>> C().foo()
459called A.foo()
460"""
461
462__test__ = {"tut1": test_1,
463            "tut2": test_2,
464            "tut3": test_3,
465            "tut4": test_4,
466            "tut5": test_5,
467            "tut6": test_6,
468            "tut7": test_7,
469            "tut8": test_8}
470
471# Magic test name that regrtest.py invokes *after* importing this module.
472# This worms around a bootstrap problem.
473# Note that doctest and regrtest both look in sys.argv for a "-v" argument,
474# so this works as expected in both ways of running regrtest.
475def test_main(verbose=None):
476    # Obscure:  import this module as test.test_descrtut instead of as
477    # plain test_descrtut because the name of this module works its way
478    # into the doctest examples, and unless the full test.test_descrtut
479    # business is used the name can change depending on how the test is
480    # invoked.
481    from test import support, test_descrtut
482    support.run_doctest(test_descrtut, verbose)
483
484# This part isn't needed for regrtest, but for running the test directly.
485if __name__ == "__main__":
486    test_main(1)
487