# This contains most of the executable examples from Guido's descr | |
# tutorial, once at | |
# | |
# http://www.python.org/2.2/descrintro.html | |
# | |
# 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.test_support import sortdict | |
import pprint | |
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 'test.test_descrtut.defaultdict'> | |
>>> print type(defaultdict) # its metatype | |
<type 'type'> | |
>>> a = defaultdict(default=0.0) # create an instance | |
>>> print a # show the instance | |
{} | |
>>> print type(a) # show its type | |
<class 'test.test_descrtut.defaultdict'> | |
>>> print a.__class__ # show its class | |
<class 'test.test_descrtut.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(): | |
>>> def sorted(seq): | |
... seq.sort(key=str) | |
... return seq | |
>>> print sorted(a.keys()) | |
[1, 2] | |
>>> exec "x = 3; print x" in a | |
3 | |
>>> print sorted(a.keys()) | |
[1, 2, '__builtins__', '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} | |
>>> | |
""" | |
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' | |
>>> | |
""" | |
test_3 = """ | |
Introspecting instances of built-in types | |
For instance of built-in types, x.__class__ is now the same as type(x): | |
>>> type([]) | |
<type 'list'> | |
>>> [].__class__ | |
<type 'list'> | |
>>> list | |
<type 'list'> | |
>>> isinstance([], list) | |
True | |
>>> isinstance([], dict) | |
False | |
>>> isinstance([], object) | |
True | |
>>> | |
Under the new proposal, the __methods__ attribute no longer exists: | |
>>> [].__methods__ | |
Traceback (most recent call last): | |
File "<stdin>", line 1, in ? | |
AttributeError: 'list' object has no attribute '__methods__' | |
>>> | |
Instead, you can get the same information from the list type: | |
>>> pprint.pprint(dir(list)) # like list.__dict__.keys(), but sorted | |
['__add__', | |
'__class__', | |
'__contains__', | |
'__delattr__', | |
'__delitem__', | |
'__delslice__', | |
'__doc__', | |
'__eq__', | |
'__format__', | |
'__ge__', | |
'__getattribute__', | |
'__getitem__', | |
'__getslice__', | |
'__gt__', | |
'__hash__', | |
'__iadd__', | |
'__imul__', | |
'__init__', | |
'__iter__', | |
'__le__', | |
'__len__', | |
'__lt__', | |
'__mul__', | |
'__ne__', | |
'__new__', | |
'__reduce__', | |
'__reduce_ex__', | |
'__repr__', | |
'__reversed__', | |
'__rmul__', | |
'__setattr__', | |
'__setitem__', | |
'__setslice__', | |
'__sizeof__', | |
'__str__', | |
'__subclasshook__', | |
'append', | |
'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 test.test_descrtut.C 1 | |
>>> c = C() | |
>>> c.foo(1) | |
classmethod test.test_descrtut.C 1 | |
>>> class D(C): | |
... pass | |
>>> D.foo(1) | |
classmethod test.test_descrtut.D 1 | |
>>> d = D() | |
>>> d.foo(1) | |
classmethod test.test_descrtut.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 test.test_descrtut.C 1 | |
>>> e = E() | |
>>> e.foo(1) | |
E.foo() called | |
classmethod test.test_descrtut.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). | |
""" | |
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 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 | |
<type '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: # classic 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 A.save() | |
>>> class A(object): # new 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() | |
Traceback (most recent call last): | |
... | |
TypeError: unbound method foo() must be called with B instance as first argument (got C instance instead) | |
>>> 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} | |
# Magic test name that regrtest.py invokes *after* importing this module. | |
# This worms around a bootstrap problem. | |
# Note that doctest and regrtest both look in sys.argv for a "-v" argument, | |
# so this works as expected in both ways of running regrtest. | |
def test_main(verbose=None): | |
# Obscure: import this module as test.test_descrtut instead of as | |
# plain test_descrtut because the name of this module works its way | |
# into the doctest examples, and unless the full test.test_descrtut | |
# business is used the name can change depending on how the test is | |
# invoked. | |
from test import test_support, test_descrtut | |
test_support.run_doctest(test_descrtut, verbose) | |
# This part isn't needed for regrtest, but for running the test directly. | |
if __name__ == "__main__": | |
test_main(1) |