skywalker212 发表于 2022-10-28 09:37:29

Python 第二版 6.3.7装饰器(78页)的例子

本帖最后由 skywalker212 于 2022-10-28 10:03 编辑

小甲鱼用了下面这个例子引出装饰器的概念,请问可不可以做如下修改,修改后内存调用或传参等运行过程是不是一样?
def log(func):
        def wrapper():
                print("开始调用eat()函数。。。")
                func()
                print("结束调用eat()函数。。。")
        return wrapper
       
def eat():
        print("开始吃了")       
eat = log(eat)
eat()
————————
将eat = log(eat)
   eat()
替换为log(eat)()
或者是 a = log(eat)
             a()
为什么小甲鱼用这种 eat = log(eat)
                                    eat()
的方式,我觉着不好理解。

jackz007 发表于 2022-10-28 09:50:51

def log(func):
      def wrapper():
                print("开始调用eat()函数。。。")
                func()
                print("结束调用eat()函数。。。")
      return wrapper
@log      
def eat():
      print("开始吃了")

eat()
      什么线内线外的,完全听不懂,这么说吧,就这个完整的例子,你准备怎么改?

skywalker212 发表于 2022-10-28 10:04:17

jackz007 发表于 2022-10-28 09:50
什么线内线外的,完全听不懂,这么说吧,就这个完整的例子,你准备怎么改?

我改了一下,麻烦您看下,萌新不太懂{:5_109:}

jackz007 发表于 2022-10-28 10:21:43

本帖最后由 jackz007 于 2022-10-28 11:02 编辑

eat = log(eat)

      变量 eat 获取的是 log(eat) 的返回值,也就是 log() . wrapper() 的入口地址,而 wraper() 要调用的是调用 log(eat) 时,作为参数传入的 eat() 函数,这样,新的 eat() 就不再是原来的 eat(),而是 log() . wrapper()。由 wrapper() 负责完成对原来 eat() 函数的调用。当然,你也可以改为
a = log(eat)
      但是,这样改过后,必须通过 a() 来调用 log() . wrapper(),当然,eat() 依然是未被包装的裸函数,如果是在通过这个机制为目标函数添加功能,那么,采用小甲鱼的方案,只要添加 eat = log(eat) 一句就够了,从此往后, eat() 就都是对包装函数的调用,如果按你的方案,得逐一把 eat() 替换成 a() 才行。

阿奇_o 发表于 2022-10-28 12:30:50

本帖最后由 阿奇_o 于 2022-10-28 13:38 编辑

不好理解,那是因为你暂时还不够充分理解:“名可名非常名,道可道非常道”。。

eat = log(eat)或 a = log(eat)   # 这句我们所直接看到的都只是 名字而已 ——左边的名字 绑定到了 右边名字被调用后返回的对象—— 这才是这句赋值的本质。
那么,“道”,这里的“道”是什么? —— 逻辑 —— 是函数log的逻辑:log(eat) 返回的是 一个函数对象! (你可以将这个对象 赋值/绑定到 任何合法名字上)

细节是:赋值前,左边的eat 和 右边的eat , 不是同一个函数对象。赋值后,也不是。 简而言之,其实是 eat 被重新绑定(重新赋值了)。
其作用也不同,左边的eat是 将要被重新赋值的一个名字,右边是作为log的参数要传递的 名为eat的原函数对象!

那为什么要起一个易混淆的同样的名字呢?—— 因为装饰器语法使用时(@deco,@log),其实际上就是这种同名的、重新赋值的手段。
但借助这种语法的好处之一是:你可以很方便地随时随地使用同一个装饰器函数,如记录日志,只要在那个函数定义上加个@log("扣个帽子"即可),而不再需要上面那样重新赋值的原始步骤。

ps: 什么是“函数对象”? —— Python里的“一等对象”。—— 何为“一等对象”?“一等”在哪? 何为“对象”?……   。。为啥又是这个本质又基础的老问题。。

ps2: 如果不想搞明白这些关键问题,一开始知道怎么用即可。毕竟装饰器类似“语法糖”(但实际不是),使用装饰器语法,本质只是为了更方便地使用 而已。


页: [1]
查看完整版本: Python 第二版 6.3.7装饰器(78页)的例子