This wiki is in the process of being archived due to lack of usage and the resources necessary to serve it — predominately to bots, crawlers, and LLM companies. Edits are discouraged.
Pages are preserved as they were at the time of archival. For current information, please visit python.org.
If a change to this archive is absolutely needed, requests can be made via the infrastructure@python.org mailing list.

This page is meant to be a central repository of decorator code pieces, whether useful or not <wink>. It is NOT a page to discuss decorator syntax!

Feel free to add your suggestions. Please make sure example code conforms with PEP 8.

Creating Well-Behaved Decorators / "Decorator decorator"

Note: This is only one recipe. Others include inheritance from a standard decorator (link?), the functools @wraps decorator, and a factory function such as Michele Simionato's decorator module which even preserves signature information.

   1 def simple_decorator(decorator):
   2     '''This decorator can be used to turn simple functions
   3     into well-behaved decorators, so long as the decorators
   4     are fairly simple. If a decorator expects a function and
   5     returns a function (no descriptors), and if it doesn't
   6     modify function attributes or docstring, then it is
   7     eligible to use this. Simply apply @simple_decorator to
   8     your decorator and it will automatically preserve the
   9     docstring and function attributes of functions to which
  10     it is applied.'''
  11     def new_decorator(f):
  12         g = decorator(f)
  13         g.__name__ = f.__name__
  14         g.__doc__ = f.__doc__
  15         g.__dict__.update(f.__dict__)
  16         return g
  17     # Now a few lines needed to make simple_decorator itself
  18     # be a well-behaved decorator.
  19     new_decorator.__name__ = decorator.__name__
  20     new_decorator.__doc__ = decorator.__doc__
  21     new_decorator.__dict__.update(decorator.__dict__)
  22     return new_decorator
  23 
  24 #
  25 # Sample Use:
  26 #
  27 @simple_decorator
  28 def my_simple_logging_decorator(func):
  29     def you_will_never_see_this_name(*args, **kwargs):
  30         print 'calling {}'.format(func.__name__)
  31         return func(*args, **kwargs)
  32     return you_will_never_see_this_name
  33 
  34 @my_simple_logging_decorator
  35 def double(x):
  36     'Doubles a number.'
  37     return 2 * x
  38 
  39 assert double.__name__ == 'double'
  40 assert double.__doc__ == 'Doubles a number.'
  41 print double(155)

Property Definition

These decorators provide a readable way to define properties:

   1 import sys
   2 
   3 def propget(func):
   4     locals = sys._getframe(1).f_locals
   5     name = func.__name__
   6     prop = locals.get(name)
   7     if not isinstance(prop, property):
   8         prop = property(func, doc=func.__doc__)
   9     else:
  10         doc = prop.__doc__ or func.__doc__
  11         prop = property(func, prop.fset, prop.fdel, doc)
  12     return prop
  13 
  14 def propset(func):
  15     locals = sys._getframe(1).f_locals
  16     name = func.__name__
  17     prop = locals.get(name)
  18     if not isinstance(prop, property):
  19         prop = property(None, func, doc=func.__doc__)
  20     else:
  21         doc = prop.__doc__ or func.__doc__
  22         prop = property(prop.fget, func, prop.fdel, doc)
  23     return prop
  24 
  25 def propdel(func):
  26     locals = sys._getframe(1).f_locals
  27     name = func.__name__
  28     prop = locals.get(name)
  29     if not isinstance(prop, property):
  30         prop = property(None, None, func, doc=func.__doc__)
  31     else:
  32         prop = property(prop.fget, prop.fset, func, prop.__doc__)
  33     return prop
  34 
  35 # These can be used like this:
  36 
  37 class Example(object):
  38 
  39     @propget
  40     def myattr(self):
  41         return self._half * 2
  42 
  43     @propset
  44     def myattr(self, value):
  45         self._half = value / 2
  46 
  47     @propdel
  48     def myattr(self):
  49         del self._half

Here's a way that doesn't require any new decorators:

   1 class Example(object):
   2     @apply  # doesn't exist in Python 3
   3     def myattr():
   4         doc = '''This is the doc string.'''
   5 
   6         def fget(self):
   7             return self._half * 2
   8 
   9         def fset(self, value):
  10             self._half = value / 2
  11 
  12         def fdel(self):
  13             del self._half
  14 
  15         return property(**locals())
  16     #myattr = myattr()  # works in Python 2 and 3

Yet another property decorator:

   1 try:
   2     # Python 2
   3     import __builtin__ as builtins
   4 except ImportError:
   5     # Python 3
   6     import builtins
   7 
   8 def property(function):
   9     keys = 'fget', 'fset', 'fdel'
  10     func_locals = {'doc':function.__doc__}
  11     def probe_func(frame, event, arg):
  12         if event == 'return':
  13             locals = frame.f_locals
  14             func_locals.update(dict((k, locals.get(k)) for k in keys))
  15             sys.settrace(None)
  16         return probe_func
  17     sys.settrace(probe_func)
  18     function()
  19     return builtins.property(**func_locals)
  20 
  21 #====== Example =======================================================
  22 
  23 from math import radians, degrees, pi
  24 
  25 class Angle(object):
  26     def __init__(self, rad):
  27         self._rad = rad
  28 
  29     @property
  30     def rad():
  31         '''The angle in radians'''
  32         def fget(self):
  33             return self._rad
  34         def fset(self, angle):
  35             if isinstance(angle, Angle):
  36                 angle = angle.rad
  37             self._rad = float(angle)
  38 
  39     @property
  40     def deg():
  41         '''The angle in degrees'''
  42         def fget(self):
  43             return degrees(self._rad)
  44         def fset(self, angle):
  45             if isinstance(angle, Angle):
  46                 angle = angle.deg
  47             self._rad = radians(angle)

Memoize

Here's a memoizing class.

   1 import collections
   2 import functools
   3 
   4 class memoized(object):
   5    '''Decorator. Caches a function's return value each time it is called.
   6    If called later with the same arguments, the cached value is returned
   7    (not reevaluated).
   8    '''
   9    def __init__(self, func):
  10       self.func = func
  11       self.cache = {}
  12    def __call__(self, *args):
  13       if not isinstance(args, collections.Hashable):
  14          # uncacheable. a list, for instance.
  15          # better to not cache than blow up.
  16          return self.func(*args)
  17       if args in self.cache:
  18          return self.cache[args]
  19       else:
  20          value = self.func(*args)
  21          self.cache[args] = value
  22          return value
  23    def __repr__(self):
  24       '''Return the function's docstring.'''
  25       return self.func.__doc__
  26    def __get__(self, obj, objtype):
  27       '''Support instance methods.'''
  28       return functools.partial(self.__call__, obj)
  29 
  30 @memoized
  31 def fibonacci(n):
  32    "Return the nth fibonacci number."
  33    if n in (0, 1):
  34       return n
  35    return fibonacci(n-1) + fibonacci(n-2)
  36 
  37 print fibonacci(12)

Alternate memoize as nested functions

Here's a memoizing function that works on functions, methods, or classes, and exposes the cache publicly.

   1 # note that this decorator ignores **kwargs
   2 def memoize(obj):
   3     cache = obj.cache = {}
   4 
   5     @functools.wraps(obj)
   6     def memoizer(*args, **kwargs):
   7         if args not in cache:
   8             cache[args] = obj(*args, **kwargs)
   9         return cache[args]
  10     return memoizer

Here's a modified version that also respects kwargs.

   1 def memoize(obj):
   2     cache = obj.cache = {}
   3 
   4     @functools.wraps(obj)
   5     def memoizer(*args, **kwargs):
   6         key = str(args) + str(kwargs)
   7         if key not in cache:
   8             cache[key] = obj(*args, **kwargs)
   9         return cache[key]
  10     return memoizer

Alternate memoize as dict subclass

This is an idea that interests me, but it only seems to work on functions:

   1 class memoize(dict):
   2     def __init__(self, func):
   3         self.func = func
   4 
   5     def __call__(self, *args):
   6         return self[args]
   7 
   8     def __missing__(self, key):
   9         result = self[key] = self.func(*key)
  10         return result
  11 
  12 #
  13 # Sample use
  14 #
  15 
  16 >>> @memoize
  17 ... def foo(a, b):
  18 ...     return a * b
  19 >>> foo(2, 4)
  20 8
  21 >>> foo
  22 {(2, 4): 8}
  23 >>> foo('hi', 3)
  24 'hihihi'
  25 >>> foo
  26 {(2, 4): 8, ('hi', 3): 'hihihi'}

Alternate memoize that stores cache between executions

Additional information and documentation for this decorator is available on Github.

   1 import pickle
   2 import collections
   3 import functools
   4 import inspect
   5 import os.path
   6 import re
   7 import unicodedata
   8 
   9 class Memorize(object):
  10     '''
  11     A function decorated with @Memorize caches its return
  12     value every time it is called. If the function is called
  13     later with the same arguments, the cached value is
  14     returned (the function is not reevaluated). The cache is
  15     stored as a .cache file in the current directory for reuse
  16     in future executions. If the Python file containing the
  17     decorated function has been updated since the last run,
  18     the current cache is deleted and a new cache is created
  19     (in case the behavior of the function has changed).
  20     '''
  21     def __init__(self, func):
  22         self.func = func
  23         self.set_parent_file() # Sets self.parent_filepath and self.parent_filename
  24         self.__name__ = self.func.__name__
  25         self.set_cache_filename()
  26         if self.cache_exists():
  27             self.read_cache() # Sets self.timestamp and self.cache
  28             if not self.is_safe_cache():
  29                 self.cache = {}
  30         else:
  31             self.cache = {}
  32 
  33     def __call__(self, *args):
  34         if not isinstance(args, collections.Hashable):
  35             return self.func(*args)
  36         if args in self.cache:
  37             return self.cache[args]
  38         else:
  39             value = self.func(*args)
  40             self.cache[args] = value
  41             self.save_cache()
  42             return value
  43 
  44     def set_parent_file(self):
  45         """
  46         Sets self.parent_file to the absolute path of the
  47         file containing the memoized function.
  48         """
  49         rel_parent_file = inspect.stack()[-1].filename
  50         self.parent_filepath = os.path.abspath(rel_parent_file)
  51         self.parent_filename = _filename_from_path(rel_parent_file)
  52 
  53     def set_cache_filename(self):
  54         """
  55         Sets self.cache_filename to an os-compliant
  56         version of "file_function.cache"
  57         """
  58         filename = _slugify(self.parent_filename.replace('.py', ''))
  59         funcname = _slugify(self.__name__)
  60         self.cache_filename = filename+'_'+funcname+'.cache'
  61 
  62     def get_last_update(self):
  63         """
  64         Returns the time that the parent file was last
  65         updated.
  66         """
  67         last_update = os.path.getmtime(self.parent_filepath)
  68         return last_update
  69 
  70     def is_safe_cache(self):
  71         """
  72         Returns True if the file containing the memoized
  73         function has not been updated since the cache was
  74         last saved.
  75         """
  76         if self.get_last_update() > self.timestamp:
  77             return False
  78         return True
  79 
  80     def read_cache(self):
  81         """
  82         Read a pickled dictionary into self.timestamp and
  83         self.cache. See self.save_cache.
  84         """
  85         with open(self.cache_filename, 'rb') as f:
  86             data = pickle.loads(f.read())
  87             self.timestamp = data['timestamp']
  88             self.cache = data['cache']
  89 
  90     def save_cache(self):
  91         """
  92         Pickle the file's timestamp and the function's cache
  93         in a dictionary object.
  94         """
  95         with open(self.cache_filename, 'wb+') as f:
  96             out = dict()
  97             out['timestamp'] = self.get_last_update()
  98             out['cache'] = self.cache
  99             f.write(pickle.dumps(out))
 100 
 101     def cache_exists(self):
 102         '''
 103         Returns True if a matching cache exists in the current directory.
 104         '''
 105         if os.path.isfile(self.cache_filename):
 106             return True
 107         return False
 108 
 109     def __repr__(self):
 110         """ Return the function's docstring. """
 111         return self.func.__doc__
 112 
 113     def __get__(self, obj, objtype):
 114         """ Support instance methods. """
 115         return functools.partial(self.__call__, obj)
 116 
 117 def _slugify(value):
 118     """
 119     Normalizes string, converts to lowercase, removes
 120     non-alpha characters, and converts spaces to
 121     hyphens. From
 122     http://stackoverflow.com/questions/295135/turn-a-string-into-a-valid-filename-in-python
 123     """
 124     value = unicodedata.normalize('NFKD', value).encode('ascii', 'ignore')
 125     value = re.sub(r'[^\w\s-]', '', value.decode('utf-8', 'ignore'))
 126     value = value.strip().lower()
 127     value = re.sub(r'[-\s]+', '-', value)
 128     return value
 129 
 130 def _filename_from_path(filepath):
 131     return filepath.split('/')[-1]

Cached Properties

   1 #
   2 # © 2011 Christopher Arndt, MIT License
   3 #
   4 
   5 import time
   6 
   7 class cached_property(object):
   8     '''Decorator for read-only properties evaluated only once within TTL period.
   9 
  10     It can be used to create a cached property like this::
  11 
  12         import random
  13 
  14         # the class containing the property must be a new-style class
  15         class MyClass(object):
  16             # create property whose value is cached for ten minutes
  17             @cached_property(ttl=600)
  18             def randint(self):
  19                 # will only be evaluated every 10 min. at maximum.
  20                 return random.randint(0, 100)
  21 
  22     The value is cached  in the '_cache' attribute of the object instance that
  23     has the property getter method wrapped by this decorator. The '_cache'
  24     attribute value is a dictionary which has a key for every property of the
  25     object which is wrapped by this decorator. Each entry in the cache is
  26     created only when the property is accessed for the first time and is a
  27     two-element tuple with the last computed property value and the last time
  28     it was updated in seconds since the epoch.
  29 
  30     The default time-to-live (TTL) is 300 seconds (5 minutes). Set the TTL to
  31     zero for the cached value to never expire.
  32 
  33     To expire a cached property value manually just do::
  34 
  35         del instance._cache[<property name>]
  36 
  37     '''
  38     def __init__(self, ttl=300):
  39         self.ttl = ttl
  40 
  41     def __call__(self, fget, doc=None):
  42         self.fget = fget
  43         self.__doc__ = doc or fget.__doc__
  44         self.__name__ = fget.__name__
  45         self.__module__ = fget.__module__
  46         return self
  47 
  48     def __get__(self, inst, owner):
  49         now = time.time()
  50         try:
  51             value, last_update = inst._cache[self.__name__]
  52             if self.ttl > 0 and now - last_update > self.ttl:
  53                 raise AttributeError
  54         except (KeyError, AttributeError):
  55             value = self.fget(inst)
  56             try:
  57                 cache = inst._cache
  58             except AttributeError:
  59                 cache = inst._cache = {}
  60             cache[self.__name__] = (value, now)
  61         return value

Retry

Call a function which returns True/False to indicate success or failure. On failure, wait, and try the function again. On repeated failures, wait longer between each successive attempt. If the decorator runs out of attempts, then it gives up and returns False, but you could just as easily raise some exception.

   1 import time
   2 import math
   3 
   4 # Retry decorator with exponential backoff
   5 def retry(tries, delay=3, backoff=2):
   6   '''Retries a function or method until it returns True.
   7 
   8   delay sets the initial delay in seconds, and backoff sets the factor by which
   9   the delay should lengthen after each failure. backoff must be greater than 1,
  10   or else it isn't really a backoff. tries must be at least 0, and delay
  11   greater than 0.'''
  12 
  13   if backoff <= 1:
  14     raise ValueError("backoff must be greater than 1")
  15 
  16   tries = math.floor(tries)
  17   if tries < 0:
  18     raise ValueError("tries must be 0 or greater")
  19 
  20   if delay <= 0:
  21     raise ValueError("delay must be greater than 0")
  22 
  23   def deco_retry(f):
  24     def f_retry(*args, **kwargs):
  25       mtries, mdelay = tries, delay # make mutable
  26 
  27       rv = f(*args, **kwargs) # first attempt
  28       while mtries > 0:
  29         if rv is True: # Done on success
  30           return True
  31 
  32         mtries -= 1      # consume an attempt
  33         time.sleep(mdelay) # wait...
  34         mdelay *= backoff  # make future wait longer
  35 
  36         rv = f(*args, **kwargs) # Try again
  37 
  38       return False # Ran out of tries :-(
  39 
  40     return f_retry # true decorator -> decorated function
  41   return deco_retry  # @retry(arg[, ...]) -> true decorator

Pseudo-currying

(FYI you can use functools.partial() to emulate currying (which works even for keyword arguments))

   1 class curried(object):
   2   '''
   3   Decorator that returns a function that keeps returning functions
   4   until all arguments are supplied; then the original function is
   5   evaluated.
   6   '''
   7 
   8   def __init__(self, func, *a):
   9     self.func = func
  10     self.args = a
  11 
  12   def __call__(self, *a):
  13     args = self.args + a
  14     if len(args) < self.func.func_code.co_argcount:
  15       return curried(self.func, *args)
  16     else:
  17       return self.func(*args)
  18 
  19 
  20 @curried
  21 def add(a, b):
  22     return a + b
  23 
  24 add1 = add(1)
  25 
  26 print add1(2)

Creating decorator with optional arguments