Python装饰器有哪些常见用途?

时间:2009-01-28 22:35:03

标签: python decorator

虽然我喜欢把自己想象成一个相当称职的Python编码器,但我从来没有能够理解的语言的一个方面就是装饰者。

我知道它们是什么(表面上),我已经阅读了关于Stack Overflow的教程,示例和问题,我理解语法,可以自己编写,偶尔使用@classmethod和@staticmethod,但它永远不会发生在我身上使用装饰器来解决我自己的Python代码中的问题。我从来没有遇到过一个问题,我想,“嗯......这看起来像装饰工作!”

所以,我想知道你们是否可以提供一些你在自己的程序中使用装饰器的例子,希望我会有一个“A-ha!”时刻和得到他们。

13 个答案:

答案 0 :(得分:121)

我主要将装饰器用于计时目的

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):
    ...

答案 1 :(得分:94)

我已将它们用于同步。

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

正如评论中所指出的那样,从Python 2.5开始,您可以将with语句与threading.Lock(或自{2.6}以后的版本)对象一起使用,以简化装饰器的实现。只是:

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

无论如何,你可以这样使用它:

import threading
lock = threading.Lock()

@synchronized(lock)
def do_something():
  # etc

@synchronzied(lock)
def do_something_else():
  # etc

基本上它只是在函数调用的任一侧放置lock.acquire() / lock.release()

答案 2 :(得分:65)

我使用装饰器进行类型检查参数,这些参数通过一些RMI传递给我的Python方法。因此,不是重复相同的参数计数,而是一次又一次地重复异常mumbo-jumbo

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

我刚宣布

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

并接受()为我完成所有工作。

答案 3 :(得分:46)

装饰器用于任何你想透明地“包装”其他功能的东西。

Django使用它们来包装"login required" functionality on view functions以及registering filter functions

您可以为adding named logs to classes使用类装饰器。

任何足够通用的功能,你可以“适应”现有的类或功能的行为是公平的装饰游戏。

discussion of use cases on the Python-Dev newsgroup还指出PEP 318 -- Decorators for Functions and Methods

答案 4 :(得分:22)

Twisted库使用与生成器结合的装饰器来给出异步函数同步的错觉。例如:

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

使用它,本来可以分解成大量小回调函数的代码可以很自然地编写为单个块,使其更易于理解和维护。

答案 5 :(得分:20)

对于nosetests,你可以编写一个装饰器来提供具有多组参数的单元测试函数或方法:

@parameters(
   (2, 4, 6),
   (5, 6, 11),
)
def test_add(a, b, expected):
    assert a + b == expected

答案 6 :(得分:10)

当然,一个显而易见的用途是记录:

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()

答案 7 :(得分:8)

我主要用它们来调试(打印一个打印其参数和结果的函数的包装)和验证(例如检查参数是否是正确的类型,或者在Web应用程序的情况下,如果用户具有足够的权限称一种特殊的方法)。

答案 8 :(得分:6)

我使用以下装饰器来创建函数threadsafe。它使代码更具可读性。它几乎与John Fouhy提出的类似,但不同之处在于,它只能处理单个函数,并且不需要明确地创建锁定对象。

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

答案 9 :(得分:5)

装饰器用于定义函数的属性或用作改变它的样板;这是可能的,但反直觉的是他们返回完全不同的功能。看看这里的其他响应,似乎最常见的用途之一是限制其他进程的范围 - 无论是日志记录,分析,安全检查等。

CherryPy使用对象分派来匹配对象的URL,最终匹配方法。这些方法的装饰者发信号通知CherryPy是否允许使用这些方法。例如,改编自the tutorial

class HelloWorld:

    ...

    def secret(self):
        return "You shouldn't be here."

    @cherrypy.expose
    def index(self):
        return "Hello world!"

cherrypy.quickstart(HelloWorld())

答案 10 :(得分:5)

我最近在使用社交网络Web应用程序时使用它们。对于社区/团体,我应该授予成员资格以创建新的讨论并回复您必须成为该特定组成员的消息。所以,我写了一个装饰师@membership_required并把它放在我需要的地方。

答案 11 :(得分:1)

我使用这个装饰器来修复参数

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']

当我重构一些函数时需要传递参数“wanN”,但在我的旧代码中,我只通过了N或'N'

答案 12 :(得分:1)

可以使用Decorator轻松创建函数方法变量。

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