这个Python装饰器是如何工作的?

2 投票
3 回答
700 浏览
提问于 2025-04-16 14:42

编辑/澄清以使我的问题更具体: *我能看到装饰器静态日志函数是如何被调用的,但我不明白_是怎么被调用的,以及它的结果是如何成为日志的结果的。我能看到入口/进入的东西是怎么工作的*

class logger:
    @staticmethod
    def log(func):
        def ___(*args, **kwargs):
            try:
                print "Entering: [%s] with parameters %s" % (func.__name__, args)
                try:
                    return func(*args, **kwargs)
                except Exception, e:
                    print 'Exception in %s : %s' % (func.__name__, e)
            finally:
                print "Exiting: [%s]" % func.__name__
        return ___


class x:
      @logger.log
      def first_x_method(self):
          print 'doing first_x_method stuff...'

x().first_x_method()

输出是:

Entering: [first_x_method] with parameters (<__main__.x instance at 0x0000000001F45648>,)
doing first_x_method stuff...
Exiting: [first_x_method]

我知道logger是一个类,它有一个静态方法,用来装饰(@logger.log)第一个方法(first_x_method)。

不过我不明白为什么___这个子方法(它可以是任何名字)会被调用。

3 个回答

1
def decorate(fn):
    def wrapper():
        fn()
    print 'Wrapping function ', wrapper
    return wrapper

def x():
    pass

print 'Original x ', x

x = decorate(x)

print 'New x ', x

输出:

Original x <function x at 0x7f3c51e9a758>
Wrapping function <function wrapper at 0x7f3c51e9a7d0>
New x  <function wrapper at 0x7f3c51e9a7d0>

注意现在 xwrapper 是一样的。当你调用 x 时,其实是在调用 wrapper

不要把这个回答标记为正确答案。请把其他的某个回答标记为正确答案。这样做的目的是为了纠正一个误解,这样你才能更好地理解其他的回答。

2

装饰器是一种函数,它可以接收另一个函数作为参数,并返回一个“装饰过的”函数,这个函数在被调用时会实际执行。

在这个例子中,logger.log 接收了函数 first_x_method。实际的装饰器实现会创建一个函数,这个函数在运行它的参数之前和之后会打印一条消息,并返回这个函数(在这里叫做 ____)。所以实际上,每次你调用 first_x_method 时,你实际上是在调用 ____,而 ____ 会保持对 first_x_method 的引用,称为 func

换句话说,____ “捕获”了 first_x_method 并替换了它。每当它被调用时,首先会打印一些内容,然后调用它所引用的函数,最后再打印一些内容。

编辑:也许这个简单的例子可以帮助你理解:

def decorate(func):
  print "Decorate called"
  def internal(*args, **kwargs):
    print "Calling internal"
    func(*args, **kwargs)
    print "Original function returned: "
  print "Created the decorated function, returning."
  return internal

@decorate
def foo(s):
  print "foo called with: ", s
  return 42

foo('hello')
6

关于装饰器的基本事实是

@decorator
def func(): ...    

def func(): ...
func=decorator(func)

是完全一样的。

所以,

@logger.log
def first_x_method(self): ...

def first_x_method(self): ...
first_x_method=logger.log(first_x_method)

是相同的,这样 logger.log 这个静态方法就会用参数 func = first_x_method 被调用。

在调用 logger.log(first_x_method) 的过程中,定义并返回了一个子方法 __

因此,first_x_method=logger.log(first_x_method) 这行代码就把 first_x_method 设置为指向这个子方法 __

first_x_method() 中的括号告诉 Python 要调用这个方法 first_x_method

所以 x().first_x_method() 首先创建了一个类 x 的实例,然后调用了方法 first_x_method(并把 x() 作为第一个参数传进去)。

因为 first_x_method 指向的是 __,所以实际上调用的是 __

撰写回答