今天学习了一下python的装饰器,比较难理解的是大家举最简单的装饰例子时都是双层嵌套:
但是单层函数,不也能实现装饰功能吗?python把装饰器设置成双层的目的是什么呢?@到底代表什么运作机制。
学习是最好的投资!
如同上面好几位大大所说, 装饰器 (@语法) 的作用:
@foo def bar(): ...
等价于:
bar = foo(bar)
翻成中文就是:
利用被@ 的function 当作引数来呼叫@ function, 并且赋值给被@ function 的函数名@ 的 function 當作引數來呼叫 @ function, 並且賦值給 被 @ function 的函數名
@
因為這個動作很像是 裝飾(修改, 擴增, 調整, 限制...) 原本的 bar, 所以被叫做 裝飾器:
bar
By definition, a decorator is a function that takes another function and extends the behavior of the latter function without explicitly modifying it.
雖然說是裝飾, 但執行完的 bar 跟原本的 bar 早已不是同一個人了, foo 會返回一個全新的對象, 通常是一個 function, 但很有可能返回的東西根本連 function 都不是:
foo
def foo(func): return None @foo def bar(): ...
在上面的例子中, foo 變成了一個很奇怪的裝飾器, 因為他返還的東西是 None, 不但沒有裝飾, 還毀滅了 function
None
回到你一開始舉的例子:
def foo(func): print('foo') func() @foo def func_a(): print('func_a') func_a()
你的 foo 沒有 return 述句, 這代表 foo 會返還 None, 你寫的根本是個毀滅器(開玩笑), 你看到的效果只是曇花一現的假象, 那是因為在 @ 語法發揮作用的那一瞬間, print 語法被執行了
return
print
但是你大可以試著調用 func_a, 你會發現錯誤被引發了, 因為 func_a
func_a
原本的 bar, 所以被叫做 装饰器:
registry = [] def register(func): print('register {}'.format(func)) registry.append(func) return func # 還是應該要 return func
装饰器的样貌千千万万种, 有的的确使用单层嵌套就可以了, 比如说注册函数: rrreee
可能各位大神没明白我的意思,我的想法比较钻牛角尖,我的问题是python为什么这么设计,因为开始我觉得嵌套两层函数没有用处,一个简单的装饰器,一层函数足以能打印个log,time啥的呀,这不是python的哲学,想了一晚上,自己理解点,写的比较乱,有需要的朋友可以看看吧,惊叹于这种设计模式的精彩绝伦。使用的逆推法,也就是明确根本需求,然后倒着推理,每一步为什么这么写。
def outer(func): #3.此处关键了,因为我们是倒推,下面的结构已经固定了, def inner(x): #outer(f1)返回给f1的值,必须是一个函数!outer自己也是函数 print('loding') #可以返回自己嘛!但是注意哦,它已经有且必须有一个func参数,来传递原生函数名 func(x) #也就是f1函数名变量的入口,那么他返回给新的f1函数,就会使原生函数多个参数, print('Done') #改变我们最初的目的,新f1与老f1调用方法要无区别,那么咋整 return inner #嵌套一个函数inner,他接收f1参数,外层outer接收函数名f1 def f1(x): #至此装饰器是两层函数嵌套,当f1没有参数时,依然需要双层 print('f1 func',x) #因为必须返回一个函数,返回outer本身,就需要加func参数 @outer #而我们又追求不改变原生调用f1(),他是没有参数的 def f2(x): #所以一个装饰器必须至少双层函数嵌套,第一层传递原生函数名 print('f2 func',x) #第二层执行装饰功能,这设计真是牛逼,逆推一晚上才有点理解。 @outer def f3(x): print('f3 func',x) #2.那么F1要指向一个新函数 #并且这个函数能被F2 F3都指向 f1 = outer(f1) #所以它是一个可以传递函数名变量的函数 #f1 = outer(f1('tings')) #ps:带上参数一起传递,这也是一种可能,但是没有价值, #装饰器使用时每个都要写一遍,其实就已经不是原生方法调用了, #因为要给语法糖@输送'things'参数,不符合我们初衷。 f1('tings1') #1.首先明确我们的根本需求,外部调用方法要完全相同,这也是装饰器的意义。 f2('tings2') f3('tings3')
装饰器发生在定义而不是执行阶段.定义而不是执行阶段.
定义
执行
装饰器函数outer必须返回一个被装饰的函数, 注意它必须返回一个定义, 而不是调用
outer
被装饰的函数
调用
这个应该很多人都分享过了,我之前也写过一篇博客,题主有意思的话可以查看一下:
Python 装饰器详解
@foo def bar()
等价于 bar = foo(bar),理解这个就好了
二楼说的对,在给bar赋值的时候就已经执行函数了。
如同上面好几位大大所说, 装饰器 (@语法) 的作用:
等价于:
翻成中文就是:
因為這個動作很像是 裝飾(修改, 擴增, 調整, 限制...) 原本的
bar
, 所以被叫做 裝飾器:雖然說是裝飾, 但執行完的
bar
跟原本的bar
早已不是同一個人了,foo
會返回一個全新的對象, 通常是一個 function, 但很有可能返回的東西根本連 function 都不是:在上面的例子中,
foo
變成了一個很奇怪的裝飾器, 因為他返還的東西是None
, 不但沒有裝飾, 還毀滅了 function回到你一開始舉的例子:
你的
foo
沒有return
述句, 這代表foo
會返還None
, 你寫的根本是個毀滅器(開玩笑), 你看到的效果只是曇花一現的假象, 那是因為在@
語法發揮作用的那一瞬間,print
語法被執行了但是你大可以試著調用
因为这个动作很像是func_a
, 你會發現錯誤被引發了, 因為func_a
装饰(修改, 扩增, 调整, 限制...)
原本的
By definition, a decorator is a function that takes another function and extends the behavior of the latter function without explicitly modifying it.bar
, 所以被叫做 装饰器:bar
跟原本的bar
早已不是同一个人了,foo
会返回一个全新的对象,通常是一个function, 但很有可能返回的东西根本连function 都不是:foo
变成了一个很奇怪的装饰器, 因为他返还的东西是None
, 不但没有装饰, 还毁灭了 function 回到你一开始举的例子: rrreee 你的foo
没有return
述句, 这代表foo
会返还None
, 你写的根本是个毁灭器(开玩笑), 你看到的效果只是昙花一现的假象, 那是因为在@
语法发挥作用的那一瞬间,print
语法被执行了func_a
, 你会发现错误被引发了, 因为func_a
根本不是一个function, 当然你想要达到的效果也无法复用装饰器的确不一定要使用local function 或是嵌套式的函数, 但是我们通常都会让装饰器返还一个function, 我觉得这是很重要的一点, 毕竟我们都会直觉认为被装饰过的函数还是个函数
装饰器的样貌千千万万种, 有的的确使用单层嵌套就可以了, 比如说注册函数: rrreee
原因是这个动作只需要在装饰的时候处理一次即可, 你不会想要每次调用函数都注册一次, 我们需要的只是原来的 function. 🎜 🎜但是像🎜打印log🎜 或是🎜计算时间🎜 等等的行为, 我们还是得用嵌套的手法, 因为我们每次调用function 都想要打印和计算, 我们需要的是一个新的function, 这个function 就靠local function 制造出来, 那必然会产生层叠和嵌套, 还有一些比较复杂的带参数的装饰器, 可能会使用到超过两层嵌套🎜 🎜小结🎜 🎜要不要嵌套要看目的, 但务必记得装饰后返回函数🎜 🎜 🎜🎜我回答过的问题🎜: Python-QA🎜可能各位大神没明白我的意思,我的想法比较钻牛角尖,我的问题是python为什么这么设计,因为开始我觉得嵌套两层函数没有用处,一个简单的装饰器,一层函数足以能打印个log,time啥的呀,这不是python的哲学,想了一晚上,自己理解点,写的比较乱,有需要的朋友可以看看吧,惊叹于这种设计模式的精彩绝伦。使用的逆推法,也就是明确根本需求,然后倒着推理,每一步为什么这么写。
装饰器发生在
定义
而不是执行
阶段.定义
而不是执行
阶段.装饰器函数
outer
必须返回一个被装饰的函数
, 注意它必须返回一个定义
, 而不是调用
outer
必须返回一个被装饰的函数
, 注意它必须返回一个定义
, 而不是调用
🎜 🎜楼下讲的详细, 很不错.🎜这个应该很多人都分享过了,我之前也写过一篇博客,题主有意思的话可以查看一下:
Python 装饰器详解
等价于 bar = foo(bar),理解这个就好了
二楼说的对,在给bar赋值的时候就已经执行函数了。