Есть ли способ для декорированной функции ссылаться на объект, созданный декоратором? - PullRequest
0 голосов
/ 22 января 2019

Мне было интересно, есть ли способ для декорированной функции ссылаться на объект, созданный оберткой декоратора.Мой вопрос возник, когда я подумал использовать декоратор, чтобы:

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

Однако декорированная функция должна ссылаться на фигуру, созданную оболочкой.Как декорированная функция может ссылаться на этот объект?Обязательно ли нам прибегать к глобальным переменным?

Вот краткий пример, где я ссылаюсь в декорированной функции на переменную, созданную в обертке (но мне не удалось сделать это без настройки глобальных переменных):

def my_decorator(func):
    def my_decorator_wrapper(*args, **kwargs):
        global x
        x = 0
        print("x in wrapper:", x)
        return func(*args, **kwargs)
    return my_decorator_wrapper

@my_decorator
def decorated_func():
    global x
    x += 1
    print("x in decorated_func:", x)

decorated_func()
# prints:
# x in wrapper: 0
# x in decorated_func: 1

Я знаю, что это было бы легко сделать в классе, но я задаю этот вопрос из любопытства.

Ответы [ 3 ]

0 голосов
/ 23 января 2019

Старайтесь избегать использования глобальных переменных .

Использовать аргументы для передачи объектов в функции

Существует один канонический способ передачи значения в функцию: arguments.

Передать объект в качестве аргумента декорированной функции при вызове оболочки.

from functools import wraps

def decorator(f):
    obj = 1

    @wraps(f)
    def wrapper(*args):
        return f(obj, *args)

    return wrapper

@decorator
def func(x)
    print(x)

func() # prints 1

Использовать аргумент по умолчанию для передачи того же объекта

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

from functools import wraps

def decorator(f, obj={}):
    @wraps(f)
    def wrapper(*args):
        return f(obj, *args)

    return wrapper

@decorator
def func(params)
    params['foo'] = True

@decorator
def gunc(params)
    print(params)

func()

# proof that gunc receives the same object
gunc() # prints {'foo': True}

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

0 голосов
/ 23 января 2019

Классы как декораторы

Эта статья указывает на классы как декораторы, что представляется более элегантным способом указать на состояние, определенное в декораторе.Он опирается на атрибуты функции и использует специальный метод .__call__() в классе декорации.

Вот мой пример, в котором снова используется класс вместо функции в качестве декоратора:

class my_class_decorator:
    def __init__(self, func):
        self.func = func
        self.x =  0

    def __call__(self, *args, **kwargs):
        print("x in wrapper:", self.x)
        return self.func(*args, **kwargs)

@my_class_decorator
def decorated_func():
    decorated_func.x += 1
    print("x in decorated_func:", decorated_func.x)

decorated_func()
# prints:
# x in wrapper: 0
# x in decorated_func: 1
0 голосов
/ 23 января 2019

Да, функция может ссылаться на нее, глядя на себя.

конец декоратора. он просто берет атрибуты и устанавливает их в функции

если это выглядит немного сложно, это потому, что декораторам, которые принимают параметры, нужна эта конкретная структура для работы. см. Декораторы с параметрами?

def declare_view(**kwds):
    """declaratively assocatiate a Django View function with resources
    """

    def actual_decorator(func):
        for k, v in kwds.items():
            setattr(func, k, v)

        return func

    return actual_decorator

вызов декоратора

@declare_view(
    x=2
)
def decorated_func():
    #the function can look at its own name, because the function exists 
    #by the time it gets called.
    print("x in decorated_func:", decorated_func.x)

decorated_func()

выход

x in decorated_func: 2 

На практике я использовал это совсем немного. Идея для меня заключается в том, чтобы связать функции представления Django с конкретными классами данных и шаблонами, с которыми они должны сотрудничать. Поскольку он декларативный, я могу анализировать все представления Django и отслеживать связанные с ними URL-адреса, а также настраиваемые объекты данных и шаблоны. Работает очень хорошо, но да, функция ожидает, что определенные атрибуты будут существовать сами по себе. Он не знает, что их установил декоратор.

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

Поначалу странно, но довольно мощно и не имеет недостатков времени выполнения или обслуживания.

Вот несколько примеров из жизни.

@declare_view(
    viewmanager_cls=backend.VueManagerDetailPSCLASSDEFN,
    template_name="pssecurity/detail.html",
    objecttype=constants.OBJECTTYPE_PERMISSION_LIST[0],
    bundle_name="pssecurity/detail.psclassdefn",
)
def psclassdefn_detail(request, CLASSID, dbr=None, PORTAL_NAME="EMPLOYEE"):
    """

    """
    f_view = psclassdefn_detail
    viewmanager = f_view.viewmanager_cls(request, mdb, f_view=f_view)
    ...do things based on the parameters...
    return viewmanager.HttpResponse(f_view.template_name)
...