这个Python装饰函数的逻辑执行顺序是什么?

4

想象一下:

def b1(fnc):
  print "b1"
  return fnc

@b1
def a1():
  print "a1"

if __name__ == "__main__":
  a1() # will print b1 a1

所以,当我使用@b1时,a1会变成a1 = b1(a1),对吗? 然后,当我说:

a1()

这是需要翻译的内容:

这将变成:

b1(a1)

然后进入:
print "b1"
return fnc

fnc被调用的具体位置/来源在哪里/是谁?我感觉自己在问一个很蠢的问题,但我想要明白。

4个回答

7
装饰器仅被执行一次。它接受一个可调用对象并返回一个可调用对象。 它所返回的对象将替代a1
换句话说,在a1定义的地方调用了b1。它输出"b1"并不改变a1。因为它没有改变a1,所以在对a1进行任何后续调用时,b1都不会起任何作用。
因此,以下注释并不完全正确:
  a1() # will print b1 a1

事实上,
  • a1() 只会打印 "a1"
  • 你看到的 "b1" 是由 @b1 / def a1(): 打印出来的。

如果你修改代码并重新运行,事件的顺序应该会更加清晰:

def b1(fnc):
  print "b1"
  return fnc

@b1
def a1():
  print "a1"

if __name__ == "__main__":
  print 'in __main__'
  a1()
  a1()

最终,为了达到您想要的效果,装饰器需要返回一个不同的可调用对象:
def b1(fnc):
  def decorated():
    print "b1"
    return fnc()
  return decorated

@b1
def a1():
  print "a1"

if __name__ == "__main__":
  print 'in __main__'
  a1()
  a1()

这里很明显是由谁调用了fnc()


关键思想是函数定义只是另一段会被执行的代码(通常只会执行一次)。装饰器只在函数定义执行时才会被执行。 - Björn Pollex

1
谁在调用 fnc
基本上是代码最后一行 a1 后面的 ()
那么发生了什么?装饰器的作用是改变函数的行为。
因此,当您装饰某些东西时,会基于装饰器返回的内容创建一个新函数。这意味着装饰器在定义 a1 时运行一次。以下是演示此过程的示例:
print 'def b1'
def b1(fnc):
  print "b1"
  return fnc

print 'def a1'
@b1
def a1():
  print "a1"

if __name__ == "__main__":
  print 'main'
  a1() # will print b1 a1

这将打印:

def b1
def a1
b1
main
a1

如您所见,装饰器b1在执行main之前被调用。
它返回一个函数实例,该实例被分配给别名a1,可像任何其他函数实例一样使用。
这就是代码最后一行中a1后面的"调用运算符"()所做的事情。

0

我们称之为 a1_prev,即在装饰之前的 a1 函数,a1() 变成了

b1(a1_prev)()

不仅是b1(a1_prev),最后的()是调用由b1返回的func的那些。


0

查看如何制作函数装饰器链的最佳答案?

你会注意到装饰器定义实际上是在转换函数并返回转换后的函数。这在装饰时执行(当应用@...时)。在你给出的示例中,b1没有定义转换,但print语句仍然被执行。然后返回原始函数,因此a1不会被“重新定义”。因此,b1装饰器的作用是对a1不做任何操作。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接