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