装饰函数有没有办法引用装饰器创建的对象?

时间:2019-01-22 20:53:41

标签: python python-decorators

我想知道装饰函数是否有办法引用装饰器的包装程序创建的对象。当我考虑使用装饰器进行装饰时,我的问题出现了:

  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 :(得分:1)

是的,该函数可以通过查看自身来引用它。

装饰器端。它只是接受属性并将其设置在功能上

如果看起来有点复杂,那是因为带有参数的装饰器需要这种特殊的结构才能工作。参见Decorators with parameters?

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)

答案 1 :(得分:1)

尝试avoid using global variables

使用参数将对象传递给函数

有一种将值传递给函数的规范方法:参数。

调用包装程序时,将对象作为参数传递给修饰的函数。

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是可变的,因此更改将在函数调用中反映出来。

答案 2 :(得分:0)

装饰类

This article指向类作为装饰器,这似乎是一种指向装饰器中定义的状态的更优雅的方法。它依赖于函数属性,并在装饰类中使用特殊的.__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