useful patterns & idioms trent nelson ([email protected])[email protected]
TRANSCRIPT
![Page 2: Useful Patterns & Idioms Trent Nelson (tnelson@onresolve.com)tnelson@onresolve.com](https://reader036.vdocument.in/reader036/viewer/2022071806/56649d1a5503460f949f02ba/html5/thumbnails/2.jpg)
Overview
Initial experience learning decorators My own crude explanation/definition Some useful patterns/idioms
![Page 3: Useful Patterns & Idioms Trent Nelson (tnelson@onresolve.com)tnelson@onresolve.com](https://reader036.vdocument.in/reader036/viewer/2022071806/56649d1a5503460f949f02ba/html5/thumbnails/3.jpg)
My Steps for Learning Decorators
Fired up Python Reference Manual Look up ‘decorators’ in index
No hits Search for ‘decorators’
Found PEP 318: Decorators for Functions and Methods Nice bit of history Doesn’t provide much in the way of
education
![Page 4: Useful Patterns & Idioms Trent Nelson (tnelson@onresolve.com)tnelson@onresolve.com](https://reader036.vdocument.in/reader036/viewer/2022071806/56649d1a5503460f949f02ba/html5/thumbnails/4.jpg)
My Steps for Learning Decorators (cont.)
Stumbled upon ‘definition’ of decorators in section 7.6 of the Python Reference Manual: “A function definition may be wrapped by one or
more decorator expressions. Decorator expressions are evaluated when the function is defined, in the scope that contains the function definition. The result must be a callable, which is invoked with the function object as the only argument. The returned value is bound to the function name instead of the function object. Multiple decorators are applied in nested fashion.”
Well then...
![Page 5: Useful Patterns & Idioms Trent Nelson (tnelson@onresolve.com)tnelson@onresolve.com](https://reader036.vdocument.in/reader036/viewer/2022071806/56649d1a5503460f949f02ba/html5/thumbnails/5.jpg)
My Steps for Learning Decorators
Wouldn’t recommend this approach! Surprisingly very little examples of
how to actually write decorators in Python documentation
Had to google around to grok the concept
Would have preferred an explanation along the lines of...
![Page 6: Useful Patterns & Idioms Trent Nelson (tnelson@onresolve.com)tnelson@onresolve.com](https://reader036.vdocument.in/reader036/viewer/2022071806/56649d1a5503460f949f02ba/html5/thumbnails/6.jpg)
Decorators: My Crude Explanation by Example
Consider the following , which demonstrates two different types of decorators (one that doesn’t take any arguments, and one that does):
class Foo(object): @cache def getExpensiveResource(self): ... @dll(c_char_p, returns=c_char_p) def readSetting(self, setting): ...
First important point: code body of your decorator will differ depending on whether or not you accept arguments
![Page 7: Useful Patterns & Idioms Trent Nelson (tnelson@onresolve.com)tnelson@onresolve.com](https://reader036.vdocument.in/reader036/viewer/2022071806/56649d1a5503460f949f02ba/html5/thumbnails/7.jpg)
Decorator Without Arguments: @cache
def cache(f):
# f: function object of decorated method; has
# useful info like f.func_name for the name of
# the decorated method.
def newf(*_args, **_kwds):
# This code will be executed in lieu of the
# method you've decorated. You can call the
# decorated method via f(_args, _kwds).
...
...
return newf
![Page 8: Useful Patterns & Idioms Trent Nelson (tnelson@onresolve.com)tnelson@onresolve.com](https://reader036.vdocument.in/reader036/viewer/2022071806/56649d1a5503460f949f02ba/html5/thumbnails/8.jpg)
Decorator Without Arguments: @cache Define your decorator to
accept one parameter, ‘f’ This will be the function object
of the decorated method Has useful info like
f.func_name and f.f_frame Define another method in
the body, newf, that accepts the parameters *_args and **_kwds
Implement the body of your decorator in newf() This will be called in lieu of the
decorated method Return newf at the end of
your decorator
def cache(f):
# f: function object of decorated method; has
# useful info like f.func_name for the name of
# the decorated method.
def newf(*_args, **_kwds):
# This code will be executed in lieu of the
# method you've decorated. You can call the
# decorated method via f(_args, _kwds).
...
...
return newf
![Page 9: Useful Patterns & Idioms Trent Nelson (tnelson@onresolve.com)tnelson@onresolve.com](https://reader036.vdocument.in/reader036/viewer/2022071806/56649d1a5503460f949f02ba/html5/thumbnails/9.jpg)
Decorator With Arguments:@dll(c_char_p, returns=c_char_p)def dll(*args, **kwds):
# args[0]: c_char_p
# kwds['returns'] = c_char_p
def decorator(f):
# f: function object of decorated method; has
# useful info like f.func_name for the name of
# the decorated method.
def newf(*_args, **_kwds):
# This code will be executed in lieu of the
# method you've decorated. You can call the
# decorated method via f(_args, _kwds).
...
...
return newf
return decorator
![Page 10: Useful Patterns & Idioms Trent Nelson (tnelson@onresolve.com)tnelson@onresolve.com](https://reader036.vdocument.in/reader036/viewer/2022071806/56649d1a5503460f949f02ba/html5/thumbnails/10.jpg)
Decorator With Arguments:@dll(c_char_p, returns=c_char_p) Define your decorator as
accepting two parameters, *args and **kwds args[0]: c_char_p kwds[‘returns’]: c_char_p
Define another method that takes a single parameter, ‘f’, which will be the function object of the decorated method
Define another method, newf, that accepts *_args, **_kwds
Implement the decorator body in newf
Return newf and decorator
def dll(*args, **kwds):
# args[0]: c_char_p
# kwds['returns'] = c_char_p
def decorator(f):
# f: function object of decorated method; has
# useful info like f.func_name for the name of
# the decorated method.
def newf(*_args, **_kwds):
# This code will be executed in lieu of the
# method you've decorated. You can call th
# decorated method via f(_args, _kwds).
...
...
return newf
return decorator
![Page 11: Useful Patterns & Idioms Trent Nelson (tnelson@onresolve.com)tnelson@onresolve.com](https://reader036.vdocument.in/reader036/viewer/2022071806/56649d1a5503460f949f02ba/html5/thumbnails/11.jpg)
Summary
If you don’t accept arguments:def cache(f): def newf(*_args, **kwds): ... return newf
If you do accept arguments:def dll(*args, **kwds): def decorator(f): def newf(*_args, **kwds): ... return newf return decorator
To call the original (decorated method) in newf():result = f(_args, _kwds)
Next up: useful idioms
![Page 12: Useful Patterns & Idioms Trent Nelson (tnelson@onresolve.com)tnelson@onresolve.com](https://reader036.vdocument.in/reader036/viewer/2022071806/56649d1a5503460f949f02ba/html5/thumbnails/12.jpg)
Useful Idioms
@cache: caching results of expensive operations
@returns: casting return types to other objects
@dll: simplifying interface to a C DLL via ctypes
@db.execute, @db.select, @db.selectAll: going too far?
![Page 13: Useful Patterns & Idioms Trent Nelson (tnelson@onresolve.com)tnelson@onresolve.com](https://reader036.vdocument.in/reader036/viewer/2022071806/56649d1a5503460f949f02ba/html5/thumbnails/13.jpg)
Caching results of expensive operations: @cache Definition:def cache(f):
def newf(*_args, **_kwds):
self = _args[0]
cacheName = '_cache_' + f.func_name
cache = self.__dict__.setdefault(cacheName, dict())
# Create a string representation of our decorated method's arguments to
# use as the cache key. This ensures we only returned cached values for
# method invocations with identical arguments.
id = '%s,%s' % (repr(_args[1:]), repr(_kwds))
return cache.setdefault(id, f(*_args, **_kwds))
return newf
Sample usage:class Foo(object): @cache def expensiveOperation(foo, bar, *args, **kwds): ...
![Page 14: Useful Patterns & Idioms Trent Nelson (tnelson@onresolve.com)tnelson@onresolve.com](https://reader036.vdocument.in/reader036/viewer/2022071806/56649d1a5503460f949f02ba/html5/thumbnails/14.jpg)
@dll/@db
See demo.
![Page 15: Useful Patterns & Idioms Trent Nelson (tnelson@onresolve.com)tnelson@onresolve.com](https://reader036.vdocument.in/reader036/viewer/2022071806/56649d1a5503460f949f02ba/html5/thumbnails/15.jpg)
End of Presentation
Corresponding blog: http://blogs.onresolve.com/?p=48
Questions?