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

时间:2011-10-24 12:56:00

标签: python decorator

想象一下:

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 个答案:

答案 0 :(得分:7)

装饰器只执行一次。它需要一个可调用对象并返回一个可调用对象。它返回的对象用于代替a1

换句话说,b1在定义a1的位置被调用。它打印出"b1"并返回a1不变。由于a1保持不变,b1在后​​续a1调用中不起任何作用。

因此,以下评论不太正确:

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

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

答案 2 :(得分:0)

查看How to make a chain of function decorators?

的热门答案

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

答案 3 :(得分:0)

让我们在装饰前调用a1_prev a1函数,a1()变成

b1(a1_prev)()

而且不仅b1(a1_prev),最后()是那些拨打func返回的b1的人。