首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >python中的装饰器把我搞糊涂了。

python中的装饰器把我搞糊涂了。
EN

Stack Overflow用户
提问于 2013-01-16 08:19:37
回答 3查看 229关注 0票数 0

我有一个简单的装饰器,输出把我搞糊涂了。

代码语言:javascript
复制
def deco(func):
   def kdeco():
      print("before myfunc() called.")
      func()
      print("  after myfunc() called.")
   return kdeco

@deco
def myfunc():
   print(" myfunc() called.")

myfunc()

before myfunc() called.
 myfunc( deco(myfunc)()) called.
  after myfunc() called.

deco(myfunc)()

before myfunc() called.
before myfunc() called.
 myfunc() called.
  after myfunc() called.
  after myfunc() called.

我知道myfunc()的输出,但是deco(myfunc)()的输出把我搞糊涂了,在下面的代码中,deco(myfunc)()的输出不能是它们中的任何一个吗?

状态一:

代码语言:javascript
复制
before myfunc() called.
before myfunc() called.
 myfunc() called.
 myfunc() called.
  after myfunc() called.
  after myfunc() called.

状态二:

代码语言:javascript
复制
before myfunc() called.
 myfunc( deco(myfunc)()) called.
  after myfunc() called.
before myfunc() called.
 myfunc( deco(myfunc)()) called.
  after myfunc() called.
EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2013-01-16 08:26:49

装饰器实际上只是一个捷径,你可以手动完成一些事情。这段代码:

代码语言:javascript
复制
@deco
def myfunc():
    print(" myfunc() called.")

等同于:

代码语言:javascript
复制
def myfunc():
    print(" myfunc() called.")
myfunc = deco(myfunc)

为了讨论这一点,让我们假设原始版本仍然以original_myfunc的形式提供(即使在现实中它不是)。

所以,当你这样做的时候:

代码语言:javascript
复制
deco(myfunc)()

你最终会说的是:

代码语言:javascript
复制
deco(deco(original_myfunc))()

如果你追踪一下,就会明白为什么它会打印出你所期望的结果。但不管怎样,我们还是来做练习吧。

首先,调用deco(original_myfunc),它执行以下操作:

代码语言:javascript
复制
def kdeco():
   print("before myfunc() called.")
   original_myfunc()
   print("  after myfunc() called.")

返回kdeco

因此,这将返回一个打印“之前”的函数,调用original_myfunc,然后打印“之后”。

现在我们再次将deco(original_myfunc)传递给deco,它会这样做:

代码语言:javascript
复制
def kdeco():
   print("before myfunc() called.")
   deco(original_myfunc)()
   print("  after myfunc() called.")
return kdeco

因此,这将返回一个打印“之前”的函数,然后调用deco(original_myfunc)-which本身打印“之前”,调用original_myfunc,并打印“之后”-then打印“之后”。

这就是为什么你会得到你所做的输出。

票数 1
EN

Stack Overflow用户

发布于 2013-01-16 08:26:31

myfunc本身已经被您的装饰器包装好了。名称myfunc不再指向原始方法,它现在指向deco(myfunc)的返回值

代码语言:javascript
复制
>>> def deco(func):
...    def kdeco():
...       print("before myfunc() called.")
...       func()
...       print("  after myfunc() called.")
...    return kdeco
... 
>>> @deco
... def myfunc():
...    print(" myfunc() called.")
... 
>>> myfunc
<function kdeco at 0x10068cb18>

这是因为@decorator语法与以下内容相同:

代码语言:javascript
复制
def myfunc():
    # body of function
myfunc = deco(myfunc)

因此,myfunc已经生成了before .... calledafter ..这几行代码。现在你再把它包起来。包装器打印before ..,调用包装的myfunc (本身已经包装),它依次打印before .... calledafter ..,然后包装器打印after ..

在图表中:

代码语言:javascript
复制
call wrapped myfunc():
    kdeco: "before .."
    call original myfunc()
        original myfunc: ".. called"
    kdeco: "after .."

call deco(myfunc)():
    kdeco: "before .."
    call wrapped myfunc():
        kdeco: "before .."
        call original myfunc()
            original myfunc: ".. called"
        kdeco: "after .."
    kdeco: "after .."
票数 4
EN

Stack Overflow用户

发布于 2013-01-16 08:24:36

修饰后的函数只被调用一次,因此它不能声明被调用了两次。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/14349426

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档