Каковы некоторые распространенные применения декораторов Python?

В то время как мне нравится думать о себе как достаточно компетентном кодере Python, один из аспектов языка, который я никогда не мог найти, - это декораторы.

Я знаю, что они (поверхностно), я читал учебники, примеры, вопросы о переполнении стека, и я понимаю синтаксис, могу написать свой собственный, иногда использовать @classmethod и @staticmethod, но это никогда не происходит со мной использовать декоратор для решения проблемы в моем собственном коде Python. Я никогда не сталкиваюсь с проблемой, когда думаю: "Хм... это похоже на работу для декоратора!"

Итак, мне интересно, если вы, ребята, можете предложить несколько примеров того, где вы использовали декораторы в своих собственных программах, и, надеюсь, у меня будет "A-ha!". момент и получить их.

Ответ 1

Я использую декораторы в основном для целей времени

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

Ответ 2

Я использовал их для синхронизации.

def synchronized(lock):
    """ Synchronization decorator """
    def wrap(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), чтобы упростить реализацию декоратора просто:

def synchronized(lock):
    """ Synchronization decorator """
    def wrap(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() по обе стороны вызова функции.

Ответ 3

Я использую декораторы для параметров проверки типов, которые передаются моим методам Python через некоторый RMI. Поэтому вместо повторения одного и того же подсчета параметров снова и снова создавая исключение mumbo-jumbo

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

Я просто объявляю

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

и принимает() делает всю работу для меня.

Ответ 4

Декораторы используются для всего, что вы хотите прозрачно "обернуть" дополнительными функциями.

Django использует их для обертывания "login required" в функциях просмотра, а также для регистрация функций фильтра.

Вы можете использовать декораторы классов для добавления имен журналов в классы.

Любая достаточно универсальная функциональность, которую вы можете "прикоснуться" к существующему поведению класса или функции, - это справедливая игра для украшения.

Также существует обсуждение вариантов использования в новостной группе Python-Dev, на которую указывает PEP 318 - Декораторы для функций и методов.

Ответ 5

Библиотека Twisted использует декораторы в сочетании с генераторами, чтобы создать иллюзию синхронности асинхронной функции. Например:

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

Используя это, код, который был бы разбит на тонну небольших функций обратного вызова, может быть написан совершенно естественно как один блок, что значительно облегчает его понимание и поддержку.

Ответ 6

Для nosetests вы можете написать декоратор, который поставляет функцию или метод unit test с несколькими наборами параметров:

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

Ответ 7

В Python wiki предлагается ряд предлагаемых вариантов и фрагментов.

Ответ 8

Одно очевидное использование для ведения журнала, конечно:

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

Ответ 9

Я использую их главным образом для отладки (обертки вокруг функции, которая печатает свои аргументы и результат) и проверки (например, для проверки правильности типа аргумента или, в случае веб-приложения, если у пользователя есть достаточные привилегии вызовите конкретный метод).

Ответ 10

Недавно у меня был один из тех "А-ха!" моменты, как вы их называете, и использовали декоратор, чтобы разрешить мне профилировать только украшенные функции/методы. Это decorator_func в этот файл, выход которого можно просмотреть в KCacheGrind. Очень полезно.

Ответ 11

Я использую следующий декоратор для создания функции threadafe. Это делает код более читаемым. Это почти похоже на предложение, предложенное Джоном Фухи, но разница в том, что одна работает над одной функцией и что нет необходимости создавать объект блокировки явно.

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

Ответ 12

Декораторы используются либо для определения свойств функции, либо как шаблон, который ее изменяет; это возможно, но противоинтуитивно для них, чтобы вернуть совершенно разные функции. Рассматривая другие ответы здесь, кажется, что одним из наиболее распространенных применений является ограничение объема какого-либо другого процесса - будь то протоколирование, профилирование, проверки безопасности и т.д.

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

Ответ 13

Я использовал их недавно, работая в веб-приложении для социальных сетей. Для сообщества/групп я должен был дать разрешение на членство, чтобы создать новое обсуждение и ответить на сообщение, которое вы должны быть членом этой конкретной группы. Итак, я написал декоратор @membership_required и поставлю его там, где мне нужно было на мой взгляд.

Ответ 14

Я использую этот декоратор для исправления параметра

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"

Ответ 15

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