374

While I like to think of myself as a reasonably competent Python coder, one aspect of the language I've never been able to grok is decorators.

I know what they are (superficially), I've read tutorials, examples, questions on Stack Overflow, and I understand the syntax, can write my own, occasionally use @classmethod and @staticmethod, but it never occurs to me to use a decorator to solve a problem in my own Python code. I never encounter a problem where I think, "Hmm...this looks like a job for a decorator!"

So, I'm wondering if you guys might offer some examples of where you've used decorators in your own programs, and hopefully I'll have an "A-ha!" moment and get them.

tshepang
12.5k25 gold badges98 silver badges140 bronze badges
asked Jan 28, 2009 at 22:35
3
  • 7
    Also, decorators are useful for Memoizing - that is caching a slow-to-compute result of a function. The decorator can return a function that checks the inputs, and if they have already been presented, return a cached result. Commented Oct 25, 2014 at 21:37
  • 2
    Note that Python has a built-in decorator, functools.lru_cache, which does exactly what Peter said, since Python 3.2, released in February 2011. Commented Jul 30, 2019 at 4:30
  • The Contents of the Python Decorator Library should give you a good idea of other uses for them. Commented Nov 27, 2019 at 0:21

13 Answers 13

142

I use decorators mainly for timing purposes

def time_dec(func):
 def wrapper(*arg):
 t = time.clock()
 res = func(*arg)
 print func.func_name, time.clock()-t
 return res
 return wrapper
@time_dec
def myFunction(n):
 ...
answered Jan 29, 2009 at 1:54
4
  • 17
    Under Unix, time.clock() measures CPU time. You might want to use time.time() instead if you want to measure wall-clock time. Commented Feb 4, 2013 at 17:45
  • 26
    Great example! No idea what it does though. An explanation what you are doing there, and how the decorator solves the problem would be very nice. Commented Jun 15, 2014 at 16:00
  • 9
    Well, it measures the time it takes for myFunction to run ... Commented Jun 14, 2015 at 22:12
  • 1
    @time_dec is syntatic sugar for: myFunction = time_dec(myFunction). The rest is standard python Commented Apr 16, 2021 at 23:34
104

I've used them for synchronization.

import functools
def synchronized(lock):
 """ Synchronization decorator """
 def wrap(f):
 @functools.wraps(f)
 def newFunction(*args, **kw):
 lock.acquire()
 try:
 return f(*args, **kw)
 finally:
 lock.release()
 return newFunction
 return wrap

As pointed out in the comments, since Python 2.5 you can use a with statement in conjunction with a threading.Lock (or multiprocessing.Lock since version 2.6) object to simplify the decorator's implementation to just:

import functools
def synchronized(lock):
 """ Synchronization decorator """
 def wrap(f):
 @functools.wraps(f)
 def newFunction(*args, **kw):
 with lock:
 return f(*args, **kw)
 return newFunction
 return wrap

Regardless, you then use it like this:

import threading
lock = threading.Lock()
@synchronized(lock)
def do_something():
 # etc
@synchronzied(lock)
def do_something_else():
 # etc

Basically it just puts lock.acquire() / lock.release() on either side of the function call.

Aran-Fey
43.8k13 gold badges113 silver badges161 bronze badges
answered Jan 29, 2009 at 0:55
6
  • 23
    Possibly justified, but decorators are inherently confusing, esp. to first-year noobs who come behind you and try to mod your code. Avoid this with simplicity: just have do_something() enclose its code in a block under 'with lock:' and everyone can clearly see your purpose. Decorators are vastly overused by people wanting to seem smart (and many actually are) but then the code comes to mere mortals and gets effed-up. Commented Nov 25, 2014 at 16:40
  • 22
    @KevinJ.Rice Constraining your code so that 'first-year noobs' can better understand it is terrible practice. Decorator syntax is far easier to read and greatly decouples the code. Commented Feb 4, 2015 at 20:02
  • 21
    @TaylerJones, code readability is just about my highest priority when writing. Code is read 7+ times for every time it's modified. Hard to understand code (for noobs or for experts who are working under time pressure) is technical debt that has to be paid every time someone visits the source tree. Commented Feb 5, 2015 at 20:28
  • 1
    @TaylerJones One of the most important tasks for a programmer is to deliver clarity. Commented Jun 23, 2019 at 20:15
  • 1
    @JDOaktown one important task for programmers is to actually be able to understand simple concepts of the languages they work with.. Commented Apr 16, 2021 at 23:36
82

I use decorators for type checking parameters which are passed to my Python methods via some RMI. So instead of repeating the same parameter counting, exception-raising mumbo-jumbo again and again.

For example, instead of:

def myMethod(ID, name):
 if not (myIsType(ID, 'uint') and myIsType(name, 'utf8string')):
 raise BlaBlaException() ...

I just declare:

@accepts(uint, utf8string)
def myMethod(ID, name):
 ...

and accepts() does all the work for me.

martineau
124k29 gold badges180 silver badges317 bronze badges
answered Jan 28, 2009 at 23:49
7
  • 17
    For anyone interested, there's an implementation of @accepts in PEP 318. Commented Sep 15, 2010 at 11:23
  • 2
    I think there is typo.. the first method should be accepts.. you declared both as "myMethod" Commented Apr 29, 2014 at 13:05
  • 1
    @DevC No, it doesn't look like a typo. Since that's clearly not an implementation of "accepts(..)", and here "accepts(..)" does the work that would otherwise be done by the two lines at the start of "myMethod(..)" — that's the only interpretation that fits. Commented Oct 24, 2015 at 12:23
  • 1
    Sorry for the bump, I just wanted to point out that checking for the type of the arguments passed and raise a TypeError otherwise is considered a bad practice because it's not going to accept e.g. a int if it checks only for floats, and because normally the code itself should adapt for different kinds of values passed for maximum flexibility. Commented Apr 16, 2016 at 21:16
  • 2
    The recommended way to do type checking in Python is via the built-in isinstance() function, as it's done in the PEP 318 implementation of the decorator. Since its classinfo argument can be one or more types, using it would also mitigate @Gustavo6046's (valid) objections. Python also has an Number abstract base class, so very generic tests like isinstance(42, numbers.Number)are possible. Commented Nov 27, 2019 at 0:13
53

Decorators are used for anything that you want to transparently "wrap" with additional functionality.

Django uses them for wrapping "login required" functionality on view functions, as well as for registering filter functions.

You can use class decorators for adding named logs to classes.

Any sufficiently generic functionality that you can "tack on" to an existing class or function's behavior is fair game for decoration.

There's also a discussion of use cases on the Python-Dev newsgroup pointed to by PEP 318 -- Decorators for Functions and Methods.

answered Jan 28, 2009 at 22:41
1
  • Cherrypy uses @cherrypy.expose to keep straight which functions are public and which are hidden functions. That was my first introduction and I got used to it form there. Commented Jan 8, 2015 at 2:16
33

For nosetests, you can write a decorator that supplies a unit test function or method with several sets of parameters:

@parameters(
 (2, 4, 6),
 (5, 6, 11),
)
def test_add(a, b, expected):
 assert a + b == expected
answered Jan 29, 2009 at 0:06
24

The Twisted library uses decorators combined with generators to give the illusion that an asynchronous function is synchronous. For example:

@inlineCallbacks
def asyncf():
 doStuff()
 yield someAsynchronousCall()
 doStuff()
 yield someAsynchronousCall()
 doStuff()

Using this, code that would have been broken up into a ton of little callback functions can be written quite naturally as a single block, making it a lot easier to understand and maintain.

answered Jan 28, 2009 at 22:52
20

One obvious use is for logging, of course:

import functools
def log(logger, level='info'):
 def log_decorator(fn):
 @functools.wraps(fn)
 def wrapper(*a, **kwa):
 getattr(logger, level)(fn.__name__)
 return fn(*a, **kwa)
 return wrapper
 return log_decorator
# later that day ...
@log(logging.getLogger('main'), level='warning')
def potentially_dangerous_function(times):
 for _ in xrange(times): rockets.get_rocket(NUCLEAR=True).fire()
answered Aug 22, 2013 at 15:05
12

I use them mainly for debugging (wrapper around a function that prints its arguments and result) and verification (e.g. to check if an argument is of correct type or, in the case of web application, if the user has sufficient privileges to call a particular method).

answered Jan 28, 2009 at 22:38
6

Decorators are used either to define a function's properties or as boilerplate that alters it; it's possible but counter-intuitive for them to return completely different functions. Looking at the other responses here, it seems like one of the most common uses is to limit the scope of some other process - be it logging, profiling, security checks, etc.

CherryPy uses object-dispatching to match URLs to objects and, eventually, methods. Decorators on those methods signal whether or not CherryPy is even allowed to use those methods. For example, adapted from the tutorial:

class HelloWorld:
 ...
 def secret(self):
 return "You shouldn't be here."
 @cherrypy.expose
 def index(self):
 return "Hello world!"
cherrypy.quickstart(HelloWorld())
answered Jan 28, 2009 at 23:40
2
  • This is not true. A decorator can completely change the behavior of a function. Commented Jan 29, 2009 at 3:26
  • Okay. But how often does a decorator "completely change the behavior of a function?" From what I've seen, when they're not used to specify properties, they're just used for boilerplate code. I've edited my response. Commented Jan 29, 2009 at 5:34
6

I am using the following decorator for making a function threadsafe. It makes the code more readable. It is almost similar to the one proposed by John Fouhy but the difference is that one work on a single function and that there is no need to create a lock object explicitely.

def threadsafe_function(fn):
 """decorator making sure that the decorated function is thread safe"""
 lock = threading.Lock()
 def new(*args, **kwargs):
 lock.acquire()
 try:
 r = fn(*args, **kwargs)
 except Exception as e:
 raise e
 finally:
 lock.release()
 return r
 return new
class X:
 var = 0
 @threadsafe_function 
 def inc_var(self):
 X.var += 1 
 return X.var
answered Oct 26, 2009 at 7:29
4
  • 1
    Does this mean each function, so decorated, has its own lock? Commented Jun 28, 2010 at 19:48
  • 1
    @grieve yes, every time the decorator is used (called) it creates a new lock object for the function/method being decorated. Commented Sep 15, 2010 at 11:31
  • 5
    That's really dangerous. The method inc_var() is "threadsafe" in that only one person can call it at a time. That said, since the method operates on member variable "var" and presumably other methods may also operate on member variable "var" and those accesses are not threadsafe since the lock is not shared. Doing things this way gives the user of class X a false sense of security. Commented Jun 12, 2013 at 16:03
  • Thats not thread safe until single lock is used. Commented Feb 3, 2016 at 7:46
6

I used them recently, while working on social networking web application. For Community/Groups, I was supposed to give membership authorization to create new discussion and reply to a message you have to be the member of that particular group. So, I wrote a decorator @membership_required and put that where I required in my view.

tshepang
12.5k25 gold badges98 silver badges140 bronze badges
answered Jan 29, 2009 at 7:15
2

Decorator can be used to easily create function method variables.

def static_var(varname, value):
 '''
 Decorator to create a static variable for the specified function
 @param varname: static variable name
 @param value: initial value for the variable
 '''
 def decorate(func):
 setattr(func, varname, value)
 return func
 return decorate
@static_var("count", 0)
def mainCallCount():
 mainCallCount.count += 1
answered Aug 22, 2012 at 22:47
3
  • 10
    Thank you for your example, but (apolgies) I have to say WTF - Why would you use this? It has HUGE potential for confusing people. Of course, I respect needs for edge-case uses, but you're hitting on a common problem many inexperienced Python devs have - not using classes enough. That is, just have a simple class var of count, initialize it, and use it. Noobs tend to write drop-thru (non-class-based code) and try to cope with the lack of class functionality with elaborate workarounds. Please don't? Please? sorry to harp, thank you for your answer, but you've hit a hot-button for me. Commented Nov 25, 2014 at 16:45
  • I'd be -1 on this if it showed up as a pull request for me to code review, and so I'm also -1 on this as good python. Commented Oct 3, 2017 at 2:43
  • Cute. Silly, but cute. :) I don't mind the occasional function attribute, but they're such a rare thing in typical Python code that if I'm going to use one, I'd rather do so explicitly, rather than hide it under a decorator. Commented May 23, 2018 at 16:30
1

I use this decorator to fix parameter

def fill_it(arg):
 if isinstance(arg, int):
 return "wan" + str(arg)
 else:
 try:
 # number present as string
 if str(int(arg)) == arg:
 return "wan" + arg
 else:
 # This should never happened
 raise Exception("I dont know this " + arg)
 print "What arg?"
 except ValueError, e:
 return arg
def fill_wanname(func):
 def wrapper(arg):
 filled = fill_it(arg)
 return func(filled)
 return wrapper
@fill_wanname
def get_iface_of(wanname):
 global __iface_config__
 return __iface_config__[wanname]['iface']

this written when I refactor some functions need to passed argument "wanN" but in my old codes, I passed N or 'N' only

answered Aug 10, 2012 at 9:46

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.