mirror of
				https://github.com/python/cpython.git
				synced 2025-10-26 03:04:41 +00:00 
			
		
		
		
	 e9f4d80fa6
			
		
	
	
		e9f4d80fa6
		
			
		
	
	
	
	
		
			
			Ignore linter "imported but unused" warnings in tests when the linter doesn't understand how the import is used.
		
			
				
	
	
		
			482 lines
		
	
	
	
		
			11 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			482 lines
		
	
	
	
		
			11 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| # This contains most of the executable examples from Guido's descr
 | |
| # tutorial, once at
 | |
| #
 | |
| #     https://www.python.org/download/releases/2.2.3/descrintro/
 | |
| #
 | |
| # A few examples left implicit in the writeup were fleshed out, a few were
 | |
| # skipped due to lack of interest (e.g., faking super() by hand isn't
 | |
| # of much interest anymore), and a few were fiddled to make the output
 | |
| # deterministic.
 | |
| 
 | |
| from test.support import sortdict  # noqa: F401
 | |
| import doctest
 | |
| import unittest
 | |
| 
 | |
| 
 | |
| class defaultdict(dict):
 | |
|     def __init__(self, default=None):
 | |
|         dict.__init__(self)
 | |
|         self.default = default
 | |
| 
 | |
|     def __getitem__(self, key):
 | |
|         try:
 | |
|             return dict.__getitem__(self, key)
 | |
|         except KeyError:
 | |
|             return self.default
 | |
| 
 | |
|     def get(self, key, *args):
 | |
|         if not args:
 | |
|             args = (self.default,)
 | |
|         return dict.get(self, key, *args)
 | |
| 
 | |
|     def merge(self, other):
 | |
|         for key in other:
 | |
|             if key not in self:
 | |
|                 self[key] = other[key]
 | |
| 
 | |
| test_1 = """
 | |
| 
 | |
| Here's the new type at work:
 | |
| 
 | |
|     >>> print(defaultdict)              # show our type
 | |
|     <class '%(modname)s.defaultdict'>
 | |
|     >>> print(type(defaultdict))        # its metatype
 | |
|     <class 'type'>
 | |
|     >>> a = defaultdict(default=0.0)    # create an instance
 | |
|     >>> print(a)                        # show the instance
 | |
|     {}
 | |
|     >>> print(type(a))                  # show its type
 | |
|     <class '%(modname)s.defaultdict'>
 | |
|     >>> print(a.__class__)              # show its class
 | |
|     <class '%(modname)s.defaultdict'>
 | |
|     >>> print(type(a) is a.__class__)   # its type is its class
 | |
|     True
 | |
|     >>> a[1] = 3.25                     # modify the instance
 | |
|     >>> print(a)                        # show the new value
 | |
|     {1: 3.25}
 | |
|     >>> print(a[1])                     # show the new item
 | |
|     3.25
 | |
|     >>> print(a[0])                     # a non-existent item
 | |
|     0.0
 | |
|     >>> a.merge({1:100, 2:200})         # use a dict method
 | |
|     >>> print(sortdict(a))              # show the result
 | |
|     {1: 3.25, 2: 200}
 | |
|     >>>
 | |
| 
 | |
| We can also use the new type in contexts where classic only allows "real"
 | |
| dictionaries, such as the locals/globals dictionaries for the exec
 | |
| statement or the built-in function eval():
 | |
| 
 | |
|     >>> print(sorted(a.keys()))
 | |
|     [1, 2]
 | |
|     >>> a['print'] = print              # need the print function here
 | |
|     >>> exec("x = 3; print(x)", a)
 | |
|     3
 | |
|     >>> print(sorted(a.keys(), key=lambda x: (str(type(x)), x)))
 | |
|     [1, 2, '__builtins__', 'print', 'x']
 | |
|     >>> print(a['x'])
 | |
|     3
 | |
|     >>>
 | |
| 
 | |
| Now I'll show that defaultdict instances have dynamic instance variables,
 | |
| just like classic classes:
 | |
| 
 | |
|     >>> a.default = -1
 | |
|     >>> print(a["noway"])
 | |
|     -1
 | |
|     >>> a.default = -1000
 | |
|     >>> print(a["noway"])
 | |
|     -1000
 | |
|     >>> 'default' in dir(a)
 | |
|     True
 | |
|     >>> a.x1 = 100
 | |
|     >>> a.x2 = 200
 | |
|     >>> print(a.x1)
 | |
|     100
 | |
|     >>> d = dir(a)
 | |
|     >>> 'default' in d and 'x1' in d and 'x2' in d
 | |
|     True
 | |
|     >>> print(sortdict(a.__dict__))
 | |
|     {'default': -1000, 'x1': 100, 'x2': 200}
 | |
|     >>>
 | |
| """ % {'modname': __name__}
 | |
| 
 | |
| class defaultdict2(dict):
 | |
|     __slots__ = ['default']
 | |
| 
 | |
|     def __init__(self, default=None):
 | |
|         dict.__init__(self)
 | |
|         self.default = default
 | |
| 
 | |
|     def __getitem__(self, key):
 | |
|         try:
 | |
|             return dict.__getitem__(self, key)
 | |
|         except KeyError:
 | |
|             return self.default
 | |
| 
 | |
|     def get(self, key, *args):
 | |
|         if not args:
 | |
|             args = (self.default,)
 | |
|         return dict.get(self, key, *args)
 | |
| 
 | |
|     def merge(self, other):
 | |
|         for key in other:
 | |
|             if key not in self:
 | |
|                 self[key] = other[key]
 | |
| 
 | |
| test_2 = """
 | |
| 
 | |
| The __slots__ declaration takes a list of instance variables, and reserves
 | |
| space for exactly these in the instance. When __slots__ is used, other
 | |
| instance variables cannot be assigned to:
 | |
| 
 | |
|     >>> a = defaultdict2(default=0.0)
 | |
|     >>> a[1]
 | |
|     0.0
 | |
|     >>> a.default = -1
 | |
|     >>> a[1]
 | |
|     -1
 | |
|     >>> a.x1 = 1
 | |
|     Traceback (most recent call last):
 | |
|       File "<stdin>", line 1, in ?
 | |
|     AttributeError: 'defaultdict2' object has no attribute 'x1' and no __dict__ for setting new attributes
 | |
|     >>>
 | |
| 
 | |
| """
 | |
| 
 | |
| test_3 = """
 | |
| 
 | |
| Introspecting instances of built-in types
 | |
| 
 | |
| For instance of built-in types, x.__class__ is now the same as type(x):
 | |
| 
 | |
|     >>> type([])
 | |
|     <class 'list'>
 | |
|     >>> [].__class__
 | |
|     <class 'list'>
 | |
|     >>> list
 | |
|     <class 'list'>
 | |
|     >>> isinstance([], list)
 | |
|     True
 | |
|     >>> isinstance([], dict)
 | |
|     False
 | |
|     >>> isinstance([], object)
 | |
|     True
 | |
|     >>>
 | |
| 
 | |
| You can get the information from the list type:
 | |
| 
 | |
|     >>> import pprint
 | |
|     >>> pprint.pprint(dir(list))    # like list.__dict__.keys(), but sorted
 | |
|     ['__add__',
 | |
|      '__class__',
 | |
|      '__class_getitem__',
 | |
|      '__contains__',
 | |
|      '__delattr__',
 | |
|      '__delitem__',
 | |
|      '__dir__',
 | |
|      '__doc__',
 | |
|      '__eq__',
 | |
|      '__format__',
 | |
|      '__ge__',
 | |
|      '__getattribute__',
 | |
|      '__getitem__',
 | |
|      '__getstate__',
 | |
|      '__gt__',
 | |
|      '__hash__',
 | |
|      '__iadd__',
 | |
|      '__imul__',
 | |
|      '__init__',
 | |
|      '__init_subclass__',
 | |
|      '__iter__',
 | |
|      '__le__',
 | |
|      '__len__',
 | |
|      '__lt__',
 | |
|      '__mul__',
 | |
|      '__ne__',
 | |
|      '__new__',
 | |
|      '__reduce__',
 | |
|      '__reduce_ex__',
 | |
|      '__repr__',
 | |
|      '__reversed__',
 | |
|      '__rmul__',
 | |
|      '__setattr__',
 | |
|      '__setitem__',
 | |
|      '__sizeof__',
 | |
|      '__str__',
 | |
|      '__subclasshook__',
 | |
|      'append',
 | |
|      'clear',
 | |
|      'copy',
 | |
|      'count',
 | |
|      'extend',
 | |
|      'index',
 | |
|      'insert',
 | |
|      'pop',
 | |
|      'remove',
 | |
|      'reverse',
 | |
|      'sort']
 | |
| 
 | |
| The new introspection API gives more information than the old one:  in
 | |
| addition to the regular methods, it also shows the methods that are
 | |
| normally invoked through special notations, e.g. __iadd__ (+=), __len__
 | |
| (len), __ne__ (!=). You can invoke any method from this list directly:
 | |
| 
 | |
|     >>> a = ['tic', 'tac']
 | |
|     >>> list.__len__(a)          # same as len(a)
 | |
|     2
 | |
|     >>> a.__len__()              # ditto
 | |
|     2
 | |
|     >>> list.append(a, 'toe')    # same as a.append('toe')
 | |
|     >>> a
 | |
|     ['tic', 'tac', 'toe']
 | |
|     >>>
 | |
| 
 | |
| This is just like it is for user-defined classes.
 | |
| """
 | |
| 
 | |
| test_4 = """
 | |
| 
 | |
| Static methods and class methods
 | |
| 
 | |
| The new introspection API makes it possible to add static methods and class
 | |
| methods. Static methods are easy to describe: they behave pretty much like
 | |
| static methods in C++ or Java. Here's an example:
 | |
| 
 | |
|     >>> class C:
 | |
|     ...
 | |
|     ...     @staticmethod
 | |
|     ...     def foo(x, y):
 | |
|     ...         print("staticmethod", x, y)
 | |
| 
 | |
|     >>> C.foo(1, 2)
 | |
|     staticmethod 1 2
 | |
|     >>> c = C()
 | |
|     >>> c.foo(1, 2)
 | |
|     staticmethod 1 2
 | |
| 
 | |
| Class methods use a similar pattern to declare methods that receive an
 | |
| implicit first argument that is the *class* for which they are invoked.
 | |
| 
 | |
|     >>> class C:
 | |
|     ...     @classmethod
 | |
|     ...     def foo(cls, y):
 | |
|     ...         print("classmethod", cls, y)
 | |
| 
 | |
|     >>> C.foo(1)
 | |
|     classmethod <class '%(modname)s.C'> 1
 | |
|     >>> c = C()
 | |
|     >>> c.foo(1)
 | |
|     classmethod <class '%(modname)s.C'> 1
 | |
| 
 | |
|     >>> class D(C):
 | |
|     ...     pass
 | |
| 
 | |
|     >>> D.foo(1)
 | |
|     classmethod <class '%(modname)s.D'> 1
 | |
|     >>> d = D()
 | |
|     >>> d.foo(1)
 | |
|     classmethod <class '%(modname)s.D'> 1
 | |
| 
 | |
| This prints "classmethod __main__.D 1" both times; in other words, the
 | |
| class passed as the first argument of foo() is the class involved in the
 | |
| call, not the class involved in the definition of foo().
 | |
| 
 | |
| But notice this:
 | |
| 
 | |
|     >>> class E(C):
 | |
|     ...     @classmethod
 | |
|     ...     def foo(cls, y): # override C.foo
 | |
|     ...         print("E.foo() called")
 | |
|     ...         C.foo(y)
 | |
| 
 | |
|     >>> E.foo(1)
 | |
|     E.foo() called
 | |
|     classmethod <class '%(modname)s.C'> 1
 | |
|     >>> e = E()
 | |
|     >>> e.foo(1)
 | |
|     E.foo() called
 | |
|     classmethod <class '%(modname)s.C'> 1
 | |
| 
 | |
| In this example, the call to C.foo() from E.foo() will see class C as its
 | |
| first argument, not class E. This is to be expected, since the call
 | |
| specifies the class C. But it stresses the difference between these class
 | |
| methods and methods defined in metaclasses (where an upcall to a metamethod
 | |
| would pass the target class as an explicit first argument).
 | |
| """ % {'modname': __name__}
 | |
| 
 | |
| test_5 = """
 | |
| 
 | |
| Attributes defined by get/set methods
 | |
| 
 | |
| 
 | |
|     >>> class property(object):
 | |
|     ...
 | |
|     ...     def __init__(self, get, set=None):
 | |
|     ...         self.__get = get
 | |
|     ...         self.__set = set
 | |
|     ...
 | |
|     ...     def __get__(self, inst, type=None):
 | |
|     ...         return self.__get(inst)
 | |
|     ...
 | |
|     ...     def __set__(self, inst, value):
 | |
|     ...         if self.__set is None:
 | |
|     ...             raise AttributeError("this attribute is read-only")
 | |
|     ...         return self.__set(inst, value)
 | |
| 
 | |
| Now let's define a class with an attribute x defined by a pair of methods,
 | |
| getx() and setx():
 | |
| 
 | |
|     >>> class C(object):
 | |
|     ...
 | |
|     ...     def __init__(self):
 | |
|     ...         self.__x = 0
 | |
|     ...
 | |
|     ...     def getx(self):
 | |
|     ...         return self.__x
 | |
|     ...
 | |
|     ...     def setx(self, x):
 | |
|     ...         if x < 0: x = 0
 | |
|     ...         self.__x = x
 | |
|     ...
 | |
|     ...     x = property(getx, setx)
 | |
| 
 | |
| Here's a small demonstration:
 | |
| 
 | |
|     >>> a = C()
 | |
|     >>> a.x = 10
 | |
|     >>> print(a.x)
 | |
|     10
 | |
|     >>> a.x = -10
 | |
|     >>> print(a.x)
 | |
|     0
 | |
|     >>>
 | |
| 
 | |
| Hmm -- property is builtin now, so let's try it that way too.
 | |
| 
 | |
|     >>> del property  # unmask the builtin
 | |
|     >>> property
 | |
|     <class 'property'>
 | |
| 
 | |
|     >>> class C(object):
 | |
|     ...     def __init__(self):
 | |
|     ...         self.__x = 0
 | |
|     ...     def getx(self):
 | |
|     ...         return self.__x
 | |
|     ...     def setx(self, x):
 | |
|     ...         if x < 0: x = 0
 | |
|     ...         self.__x = x
 | |
|     ...     x = property(getx, setx)
 | |
| 
 | |
| 
 | |
|     >>> a = C()
 | |
|     >>> a.x = 10
 | |
|     >>> print(a.x)
 | |
|     10
 | |
|     >>> a.x = -10
 | |
|     >>> print(a.x)
 | |
|     0
 | |
|     >>>
 | |
| """
 | |
| 
 | |
| test_6 = """
 | |
| 
 | |
| Method resolution order
 | |
| 
 | |
| This example is implicit in the writeup.
 | |
| 
 | |
| >>> class A:    # implicit new-style class
 | |
| ...     def save(self):
 | |
| ...         print("called A.save()")
 | |
| >>> class B(A):
 | |
| ...     pass
 | |
| >>> class C(A):
 | |
| ...     def save(self):
 | |
| ...         print("called C.save()")
 | |
| >>> class D(B, C):
 | |
| ...     pass
 | |
| 
 | |
| >>> D().save()
 | |
| called C.save()
 | |
| 
 | |
| >>> class A(object):  # explicit new-style class
 | |
| ...     def save(self):
 | |
| ...         print("called A.save()")
 | |
| >>> class B(A):
 | |
| ...     pass
 | |
| >>> class C(A):
 | |
| ...     def save(self):
 | |
| ...         print("called C.save()")
 | |
| >>> class D(B, C):
 | |
| ...     pass
 | |
| 
 | |
| >>> D().save()
 | |
| called C.save()
 | |
| """
 | |
| 
 | |
| class A(object):
 | |
|     def m(self):
 | |
|         return "A"
 | |
| 
 | |
| class B(A):
 | |
|     def m(self):
 | |
|         return "B" + super(B, self).m()
 | |
| 
 | |
| class C(A):
 | |
|     def m(self):
 | |
|         return "C" + super(C, self).m()
 | |
| 
 | |
| class D(C, B):
 | |
|     def m(self):
 | |
|         return "D" + super(D, self).m()
 | |
| 
 | |
| 
 | |
| test_7 = """
 | |
| 
 | |
| Cooperative methods and "super"
 | |
| 
 | |
| >>> print(D().m()) # "DCBA"
 | |
| DCBA
 | |
| """
 | |
| 
 | |
| test_8 = """
 | |
| 
 | |
| Backwards incompatibilities
 | |
| 
 | |
| >>> class A:
 | |
| ...     def foo(self):
 | |
| ...         print("called A.foo()")
 | |
| 
 | |
| >>> class B(A):
 | |
| ...     pass
 | |
| 
 | |
| >>> class C(A):
 | |
| ...     def foo(self):
 | |
| ...         B.foo(self)
 | |
| 
 | |
| >>> C().foo()
 | |
| called A.foo()
 | |
| 
 | |
| >>> class C(A):
 | |
| ...     def foo(self):
 | |
| ...         A.foo(self)
 | |
| >>> C().foo()
 | |
| called A.foo()
 | |
| """
 | |
| 
 | |
| __test__ = {"tut1": test_1,
 | |
|             "tut2": test_2,
 | |
|             "tut3": test_3,
 | |
|             "tut4": test_4,
 | |
|             "tut5": test_5,
 | |
|             "tut6": test_6,
 | |
|             "tut7": test_7,
 | |
|             "tut8": test_8}
 | |
| 
 | |
| def load_tests(loader, tests, pattern):
 | |
|     tests.addTest(doctest.DocTestSuite())
 | |
|     return tests
 | |
| 
 | |
| 
 | |
| if __name__ == "__main__":
 | |
|     unittest.main()
 |