在从上到下阅读源代码时,是否始终按照遇到的顺序调用装饰器?

时间:2014-03-15 16:19:23

标签: python python-3.x

我试图了解在从上到下阅读源代码时是否始终按照遇到的顺序调用装饰器?这是我写的一些示例代码:

def log(func):
    print(func)
    return func

class A:
    @log
    def __init__(self):
        print('__init__')

    @log
    def foo(self):
        print('foo')

    @log
    def bar(self):
        print('bar')

    @log
    def baz(self):
        print('baz')

    @log
    def qux(self):
        print('qux')

这是输出:

<function A.__init__ at 0x0000000002342840>
<function A.foo at 0x00000000023428C8>
<function A.bar at 0x0000000002342950>
<function A.baz at 0x00000000023429D8>
<function A.qux at 0x0000000002342A60>

上面的输出似乎表明装饰器是按照从上到下读取源代码时遇到的顺序调用的?

3 个答案:

答案 0 :(得分:2)

在您的情况下,他们正在阅读&#34;按顺序&#34;但每个方法只有一个装饰器。说明它们如何被读取的一种好方法是它们被链接,或者不止一个。例如:

@i_get_called_last
@i_get_called_second
@i_get_called_first
def my_decorated_function(a):
    print a
    return a

在这个例子中,你可以读到它们实际上是从&#34;从下到上阅读&#34;,意思是从最内层到最外面的方法。

答案 1 :(得分:0)

是的,但存在非常规情况:

@log
def a():
  @log
  def b():
    pass
  pass

输出:仅

<function a at 0x7fe2dfe24710>

另一个:

def log1(func):
  print('1', func)
  return func

def log2(func):
  print('2', func)
  return func

@log1
@log2
def a():
  pass

输出:

2 <function a at 0x7fe2dfe24b90>
1 <function a at 0x7fe2dfe24b90>

答案 2 :(得分:-1)

看起来你在Python中有一个更基本的东西要理解:

  1. 所有代码都是从上到下执行
  2. 即使在编译的字节代码文件中,也没有类或函数存在
  3. 在Python中按照在代码中找到的顺序创建类和函数, 每个文件都被导入。
  4. 一旦你理解了这一点,那装饰器只相当于 用装饰器的返回值替换声明的函数,即:

    @deco
    def myfunc():
       pass
    
    # is equivalent to:
    
    def myfunc():
       pass
    myfunc = deco(myfunc)
    

    你开始明白发生了什么。 因此,当Python 编译器在源代码中找到一个块时,如:

    def a():       通

    它在字节码中记录函数体的代码对象(pass语句), 和从此代码创建函数对象的字节码,以及def语句中包含的元素(本质上是对types.FunctionType的调用)。在运行代码时(在编译传递之后),此调用的返回对象将绑定到找到该函数的命名空间中的名称a。只有在这一点上,“功能对象”才存在,并且可以被调用。

    如果在def语句之前有装饰器,则从下到上调用它们 顺序,在名称实际绑定之前 - 函数名称刚刚绑定到最后一个装饰器的返回对象。

    课堂主体不同的是:

    1. 找到类块时,解释器会创建一个新的命名空间
    2. 它进入类体并按顺序开始执行那里的行(它不执行函数体,只编译然后编译为字节码)
    3. 因此,甚至可以将forwhile循环直接放在类主体中 - 这些循环将在类创建时运行(通常在导入模块时)。
    4. 并且 - 如上所述,类体内的方法被创建为执行类的函数
    5. 当类体结束时,Python调用内置的type工厂来实际创建类:参数是类名,类库和对象的名称 (在类体中找到的函数和属性)作为字典。
    6. type(或更确切地说,类'元类)的此调用的返回对象绑定到“class”语句所在的命名空间中的类名。
    7. 如果有类装饰器,则会在名称绑定步骤之前使用返回的类对象调用它们,就像函数一样。
    8. (这实际上比创建函数更简单,而且你 实际上可以在野外找到更多的代码来创建一个没有的新类 一个类体,相反,调用type,而不是创建调用FunctionType的新函数的代码

      所以 - 回答你的问题:是的 - “装饰者按照他们遇到的顺序被调用” - 但是一旦你发现了Python的运作方式,这是很自然的。

      所以,如果上面的解释有点模糊,请检查一下它的作用:

      def log(func):
          print(func.__name__)
          return func
      
      class A(object):
         for i in range(5):
             @log
             def func(self): 
                pass
             locals()["func_%d" % i] = func
      

      并与之比较:

      class B(object):
         for i in range(5):
             def func(self): 
                pass
             func.__name__ = "func_%d" % i
             locals()["func_%d" % i] = log(func)