Python装饰器有哪些常见用途?[关闭]

问题:Python装饰器有哪些常见用途?[关闭]

尽管我喜欢将自己视为一个相当称职的Python编码器,但我从来没有想到过的语言的一方面是装饰器。

我知道它们是什么(表面上),我已经阅读了有关堆栈溢出的教程,示例和问题,而且我了解语法,可以编写自己的语法,偶尔使用@classmethod和@staticmethod,但是使用装饰器来解决我自己的Python代码中的问题。我从来没有遇到过这样的问题,我想:“嗯……这看起来像是装饰工的工作!”

因此,我想知道你们是否可以提供一些示例,说明您在自己的程序中使用装饰器的位置,并希望我会收到“ A-ha!”字样。片刻,他们。

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.


回答 0

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

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

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

回答 1

我已使用它们进行同步。

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(或multiprocessing.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()放在函数调用的任一侧。

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.


回答 2

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

例如,代替:

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

我只声明:

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

accepts()为我完成所有工作。

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.


回答 3

装饰器用于您要透明“包装”其他功能的任何物品。

Django使用它们将“需要登录”功能包装在视图功能上,以及注册过滤器功能

您可以使用类装饰器将命名日志添加到类

您可以“掌握”现有类或功能行为的任何足够通用的功能,对于装饰来说都是公平的。

PEP 318指向的Python-Dev新闻组上讨论了用例-函数和方法的装饰器

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.


回答 4

对于鼻子测试,您可以编写一个装饰器,该装饰器为单元测试功能或方法提供几组参数:

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

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

回答 5

Twisted库结合使用装饰器和生成器,给人一种异步函数是同步的错觉。例如:

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

使用此功能,原本可以分解成许多小回调函数的代码可以很自然地作为一个单独的块编写,从而使理解和维护变得更加容易。

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.


回答 6

当然,一种明显的用途是用于日志记录:

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

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

回答 7

我主要将它们用于调试(包装打印其参数和结果的函数)和验证(例如,检查参数的类型是否正确,或者对于Web应用程序,如果用户具有足够的特权来调用特定参数方法)。

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


回答 8

我正在使用以下装饰器来使函数成为线程安全的。它使代码更具可读性。它几乎与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

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

回答 9

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

CherryPy使用对象分派将URL与对象以及方法进行匹配。这些方法上的装饰器会发出信号,表明是否甚至允许 CherryPy 使用这些方法。例如,根据本教程改编而成:

class HelloWorld:

    ...

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

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

cherrypy.quickstart(HelloWorld())

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

回答 10

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

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.


回答 11

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

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’

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


回答 12

装饰器可用于轻松创建函数方法变量。

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

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