Learning Python - Mark Lutz [540]
[CCC]==> listcomp: 0.00003, 0.00003
[CCC]==> listcomp: 0.00640, 0.00643
[CCC]==> listcomp: 0.08687, 0.09330
[CCC]==> listcomp: 0.17911, 0.27241
[0, 2, 4, 6, 8]
allTime = 0.272407666337
[MMM]==> mapcall: 0.00004, 0.00004
[MMM]==> mapcall: 0.01340, 0.01343
[MMM]==> mapcall: 0.13907, 0.15250
[MMM]==> mapcall: 0.27907, 0.43157
[0, 2, 4, 6, 8]
allTime = 0.431572169089
map/comp = 1.584
As usual, we can also test this interactively to see how the configuration arguments come into play:
>>> from mytools import timer
>>> @timer(trace=False) # No tracing, collect total time
... def listcomp(N):
... return [x * 2 for x in range(N)]
...
>>> x = listcomp(5000)
>>> x = listcomp(5000)
>>> x = listcomp(5000)
>>> listcomp
>>> listcomp.alltime 0.0051938863738243413 >>> @timer(trace=True, label='\t=>') # Turn on tracing ... def listcomp(N): ... return [x * 2 for x in range(N)] ... >>> x = listcomp(5000) => listcomp: 0.00155, 0.00155 >>> x = listcomp(5000) => listcomp: 0.00156, 0.00311 >>> x = listcomp(5000) => listcomp: 0.00174, 0.00486 >>> listcomp.alltime 0.0048562736325408196 This timing function decorator can be used for any function, both in modules and interactively. In other words, it automatically qualifies as a general-purpose tool for timing code in our scripts. Watch for another example of decorator arguments in the section Implementing Private Attributes, and again in A Basic Range-Testing Decorator for Positional Arguments. * * * Note * * * Coding Class Decorators So far we’ve been coding function decorators to manage function calls, but as we’ve seen, Python 2.6 and 3.0 extend decorators to work on classes too. As described earlier, while similar in concept to function decorators, class decorators are applied to classes instead—they may be used either to manage classes themselves, or to intercept instance creation calls in order to manage instances. Also like function decorators, class decorators are really just optional syntactic sugar, though many believe that they make a programmer’s intent more obvious and minimize erroneous calls. Singleton Classes Because class decorators may intercept instance creation calls, they can be used to either manage all the instances of a class, or augment the interfaces of those instances. To demonstrate, here’s a first class decorator example that does the former—managing all instances of a class. This code implements the classic singleton coding pattern, where at most one instance of a class ever exists. Its singleton function defines and returns a function for managing instances, and the @ syntax automatically wraps up a subject class in this function: instances = {} def getInstance(aClass, *args): # Manage global table if aClass not in instances: # Add **kargs for keywords instances[aClass] = aClass(*args) # One dict entry per class return instances[aClass] def singleton(aClass): # On @ decoration def onCall(*args): # On instance creation return getInstance(aClass, *args) return onCall To use this, decorate the classes for which you want to enforce a single-instance model: @singleton # Person = singleton(Person) class Person: # Rebinds Person to onCall def __init__(self, name, hours, rate): # onCall remembers Person self.name = name self.hours = hours self.rate = rate def pay(self): return self.hours * self.rate @singleton # Spam = singleton(Spam) class Spam: # Rebinds Spam to onCall def __init__(self, val): # onCall remembers Spam self.attr = val bob = Person('Bob', 40, 10) # Really calls onCall print(bob.name,
Timing methods: This section’s timer decorator works on any function, but a minor rewrite is required to be able to apply it to class methods too. In short, as our earlier section Class Blunders I: Decorating Class Methods illustrated, it must avoid using a nested class. Because this mutation will be a subject of one of our end-of-chapter quiz questions, though, I’ll avoid giving away the answer completely here.