【问题标题】:Changing values in decorator for nested functions在装饰器中更改嵌套函数的值
【发布时间】:2015-08-19 14:48:27
【问题描述】:

我有一个类似的案例-

flag = True
print "Before All things happened, flag is", flag
def decorator(*a):
    def real_decorator(function):
        def wrapper(*args, **kwargs):
            global flag
            flag = False
            print "just in real decorator before function call i.e. before", function.__name__
            print "flag is " , flag
            function(*args, **kwargs)
            print "In real decorator after function call i.e. after", function.__name__
            flag = True
            print "flag is ", flag
        return wrapper
    return real_decorator

@decorator()
def subtota():
    print "in subtota"
    print "flag is" , flag

@decorator()
def print_args(*args):
    print "in print args"
    for arg in args:
        print arg
    print "flag is ", flag
    subtota()
    print "we do want flag to be false here, after subtota"
    print "but, flag is ", flag


print_args("bilbo", "baggins")

print "after All things happended flag is ", flag

输出是

Before All things happened, flag is True
just in real decorator before function call i.e. before print_args
flag is  False
in print args
bilbo
baggins
flag is  False
just in real decorator before function call i.e. before subtota
flag is  False
in subtota
flag is False
In real decorator after function call i.e. after subtota
flag is  True
we do want flag to be false here, after subtota
but, flag is  True
In real decorator after function call i.e. after print_args
flag is  True
after All things happended flag is  True

在这里,我不想更改subtota() 之后的标志值,或者我们可以说,我们希望保持每个函数的行为彼此独立。 我们怎样才能做到这一点? PS - 无法避免使用模块级全局变量flag

EDIT-期望的行为- 只有在最上面的函数执行后,标志才应该为假。

【问题讨论】:

  • 那么您希望在哪里输出呢?我已经尝试回答了,但是添加对预期行为的描述会很有帮助。

标签: python python-2.7 decorator python-decorators globals


【解决方案1】:

我觉得你的目标有点不清楚。

如果您需要跟踪每个函数的状态,您可以在装饰函数对象本身上设置一个标志:

def decorator(*a):
    def real_decorator(function):
        def wrapper(*args, **kwargs):
            function.flag = False
            function(*args, **kwargs)
            function.flag = True
        return wrapper
    return real_decorator

您也可以在此处将其设置为wrapper,以使该标志在装饰版本上可用:

wrapper.flag = False

如果您只需要在进入和退出最外面的装饰调用时切换标志,您可以使用单独的全局来计算您的级别;你也可以在decorator函数上设置:

def decorator(*a):
    decorator._depth = 0
    def real_decorator(function):
        def wrapper(*args, **kwargs):
            global flag
            if decorator._depth == 0:  # entering outermost call
                flag = False

            decorator._depth += 1
            function(*args, **kwargs)
            decorator._depth -= 1

            if decorator._depth == 0:  # exiting outermost call
                flag = True
        return wrapper
    return real_decorator

【讨论】:

  • 不错!但是有一个问题! flag 是一个全局变量。如果我尝试在其他函数中使用装饰器外部的function.flag 值(这反过来又访问flag var),我不会得到当前值,对吧?
  • @malhar:在这种情况下将其设置为wrapper。现在print_args.flag 为修饰的print_args 函数引用了完全相同的值,而subtota.flag 是另一个标志。
  • Martjin,这似乎不起作用,因为我的 flag var 是全局变量并用于另一个函数。我可以在装饰器之外定义的其他函数中使用wrapped.flag 吗?
  • @malhar:添加了堆栈跟踪选项;计算你有多少层,只有在最外层调用时才切换全局。
【解决方案2】:

将flag的旧值保存在包装器中,并恢复到它而不是True

【讨论】:

    【解决方案3】:

    除了使用限定于装饰器的变量外,我尝试使用以下方法-

    def decorator(*a):
    def real_decorator(function):
        def wrapper(*args, **kwargs):
            global flag
            old_flag = flag
            flag = False
            function(*args, **kwargs)
            flag = old_flag
        return wrapper
    return real_decorator
    

    这看起来有点简单,而且效果很好。

    【讨论】:

      猜你喜欢
      • 2015-06-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-09-13
      • 2021-12-05
      • 2016-11-18
      • 2011-11-04
      相关资源
      最近更新 更多