python中的用法_Python中使用@的理解
Python函數中使用@
稍提一下的基礎
fun 和fun()的區別
以一段代碼為例:
def fun():
print('fun')
return None
a = fun() #fun函數并將返回值給a
print('a的值為',a)
b = fun #將fun函數地址賦給b
b() #調用b,b和fun指向的地址相同
print('b的值為',b)
'''輸出
fun
a的值為 None
fun
b的值為
'''
根據輸出可以看出,a=fun()是將函數fun的返回值(None)賦給a,而b=fun是將函數的地址賦給b,如果調用函數,需要b()
類似的,其他內置函數也可以通過這種方法,相當于起了一個同名的函數
>>>a = abs
>>>a(-1)
1
除此之外,原來的函數名也被覆蓋為其他函數,例如
def fun():
print('fun')
abs = fun
abs() #輸出fun
綜上,可以理解為fun,abs在不帶括號時為變量,該變量包含了函數在內容的地址
返回函數
以廖老師的教程為例
def lazy_sum(*args):
def sum():
ax = 0
for n in args:
ax = ax + n
return ax
return sum
>>> f = lazy_sum(1, 3, 5, 7, 9)
>>> f
.sum at 0x101c6ed90>
>>>f()
25
在單步調試中可以發現,當讀到def sum():時,解釋器會直接跳到return sum將sum函數的地址返回給f,因此f()即為執行sum() (不是非常準確,但先如此理解)
如果對返回函數還是有些不理解的話,可以假設lazy_sum()的返回值改為1
def lazy_sum(*args):
def sum():
ax = 0
for n in args:
ax = ax + n
return ax
return 1
f = lazy_sum(1,3,5,7,9)
print(f)#Q輸出1
print(f())#報錯'int' object is not callable
此時無論lazy_sum()的參數如何修改,都會把1賦給f,而1()是不可調用的,因此會報錯
?返回函數中的閉包問題也要了解一下,內嵌函數可以訪問外層函數的變量
參數的嵌套調用
仍然上述例子,此時將lazy_sum()改為空函數,內嵌的sum()需要參數:
def lazy_sum():
def sum(*args):
ax = 0
for n in args:
ax = ax + n
return ax
return sum
f = lazy_sum()(1,3,5,7,9)
print(f)#輸出25
按照運算的優先級,可以理解為:
執行 lazy_sum(),返回sum;
執行sum(1,3,5,7,9),返回25;
將25賦給f
如果有了以上基礎,再來看@的用法就會覺得很容易了
@的使用
如果需要具體理解裝飾器,可以參考廖老師的博客,本文僅介紹@的執行流程
不帶參數的單一使用(一個@修飾)
def spamrun(fn):
def sayspam():
print("spam,spam,spam")
fn()
return sayspam
@spamrun
def useful():
print('useful')
useful()
'''
輸出:
spam,spam,spam
useful
'''
修飾效果相當于useful = spamrun(useful),具體步驟如下:
在初始化時,解釋器讀到@spamrun,此時將下方的useful作為參數傳入到spamrun中
spamrun(useful)中,由于是返回函數,直接將sayspam()的內存地址賦給useful
執行useful(),此時useful指向了sayspam,因此打印spam,spam,spam。然后執行fn(),此時的fn才指向原來的useful()的地址,開始執行print('useful')
執行流程可以在下圖了解一下,可以理解為經過@后,useful已經不直接指向函數useful()的地址了,而是sayspam。再調用useful()時,執行sayspam(),由于fn保存原先useful()函數的地址,因此可以執行useful()的功能,即可以打印出'useful'。如果‘使壞’把fn()去掉的話,相當于useful()再也不會執行了
一般情況下,使用@時不會改變函數原先的執行邏輯,而只是增加功能,因此成為裝飾器,如廖老師教程中可以使原函數打印日志
def log(func):
def wrapper(*args, **kw):
print('call %s():' % func.__name__)
return func(*args, **kw)
return wrapper
@log
def now():
print('2015-3-25')
now()
'''
call now():
2015-3-25
'''
不帶參數的多次使用(兩個@)
def spamrun(fn):
def sayspam():
print("spam,spam,spam")
fn()
return sayspam
def spamrun1(fn):
def sayspam1():
print("spam1,spam1,spam1")
fn()
return sayspam1
@spamrun
@spamrun1
def useful():
print('useful')
useful()
'''
spam,spam,spam
spam1,spam1,spam1
useful
'''
修飾效果相當于useful = spamrun(spamrun1(useful))
疊加使用時,裝飾器的調用順序和聲明順序是相反的,可以理解成是一個遞歸的過程。
遇到@spamrun,開始向下尋找def 函數名
結果第二行也是一個@。@spamrun1繼續向下找
遇到了def useful,執行useful = spamrun1(useful)
回歸。@spamrun1返回useful給@spamrun,執行useful=spamrun(useful)
帶參數的單次使用
以廖老師教程中的舉例,簡化一些,先不考慮*args,**kw,因為涉及到返回函數的閉包問題
def log(text):
def decorator(func):
def wrapper():
print('%s %s():' % (text, func.__name__))
return func()
return wrapper
return decorator
@log('execute')
def now():
print('2015-3-25')
now()
修飾效果相當于now=log('execute')(now)
?1. 解釋器讀到@log('execute'),先執行了log('execute'),返回函數decorator
?2. 將now作為decorator(func)的形參,返回warpper
3. 將`warpper`的內存地址賦給變量`now`
此時調用now(),先執行完print(...),然后return func()。注意此處是帶括號的,因此執行了真正的now函數,最終return的為None
帶參數的多次調用可以將之前的情況組合即可
總結
@行不帶參數
@XXX
def funXXX():
會被解釋成funXXX = XXX(funXXX)
如果@那行中帶參數,則被解釋成funXXX = XXX(@行的參數)(funXXX)
要深刻理解返回函數以及fun和fun()的區別
函數的內存地址,函數變量,函數的名稱的區別。默認情況下,函數變量指向函數的內存地址,但也可以被改變
初學Python,學識短淺,希望多多交流
與50位技術專家面對面20年技術見證,附贈技術全景圖總結
以上是生活随笔為你收集整理的python中的用法_Python中使用@的理解的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 中运用_胶水在木雕中的运用技巧
- 下一篇: sql 获取两个月内数据_如何在3个月的