鱼C论坛

 找回密码
 立即注册
查看: 3311|回复: 4

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

[复制链接]
发表于 2022-10-28 09:37:29 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
本帖最后由 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()
的方式,我觉着不好理解。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2022-10-28 09:50:51 | 显示全部楼层
def log(func):
        def wrapper():
                print("开始调用eat()函数。。。")
                func()
                print("结束调用eat()函数。。。")
        return wrapper
@log        
def eat():
        print("开始吃了")

eat()
        什么线内线外的,完全听不懂,这么说吧,就这个完整的例子,你准备怎么改?
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

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

我改了一下,麻烦您看下,萌新不太懂
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 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() 才行。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

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


想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2024-9-25 21:26

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表