useful patterns & idioms trent nelson ([email protected])[email protected]

15
PYTHON DECORATORS Useful Patterns & Idioms Trent Nelson ([email protected] )

Upload: silas-ellis

Post on 18-Dec-2015

213 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: Useful Patterns & Idioms Trent Nelson (tnelson@onresolve.com)tnelson@onresolve.com

PYTHON DECORATORS

Useful Patterns & IdiomsTrent Nelson ([email protected])

Page 2: Useful Patterns & Idioms Trent Nelson (tnelson@onresolve.com)tnelson@onresolve.com

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

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

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

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

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

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

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

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

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

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

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

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

@dll/@db

See demo.

Page 15: Useful Patterns & Idioms Trent Nelson (tnelson@onresolve.com)tnelson@onresolve.com

End of Presentation

Corresponding blog: http://blogs.onresolve.com/?p=48

Questions?