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