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)
'''輸出
funa的值為 none
funb的值為
根據輸出可以看出,a=fun()是將函式fun的返回值(none)賦給a,而b=fun是將函式的位址賦給b,如果呼叫函式,需要b()
類似的,其他內建函式也可以通過這種方法,相當於起了乙個同名的函式
>>>a = abs
>>>a(-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()
在單步除錯中可以發現,當讀到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):
print('call %s():' % func.__name__)
return func(*args, **kw)
@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):
print('%s %s():' % (text, func.__name__))
return func()
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
帶引數的多次呼叫可以將之前的情況組合即可
總結@行不帶引數
@***
def fun***():
會被解釋成fun*** = ***(fun***)
如果@那行中帶引數,則被解釋成fun*** = ***(@行的引數)(fun***)
要深刻理解返回函式以及fun和fun()的區別
函式的記憶體位址,函式變數,函式的名稱的區別。預設情況下,函式變數指向函式的記憶體位址,但也可以被改變
初學python,學識短淺,希望多多交流
pytho中with語句的用法
python中的with語句使用於對資源進行訪問的場合,在程式處理過程中是否異常都會執行 enter self 方法,exit 清理 方法操作,釋放被訪問的資源,比如有檔案讀寫後自動關閉 執行緒中鎖的自動獲取和釋放都可以使用。用open開啟乙個檔案進行讀寫時,都有可能產生ioerror。而且檔案每次...
Pytho高階篇 yield的用法
yield 是python中非常有用的乙個關鍵字,可以實現很多魔法。yield關鍵字主要有一下幾個用法。1.yield基本用法 yield用在函式中,實現類似用return的功能,但是返回的是乙個generator.更多詳細解釋,參考下邊的 如何正確理解yiled在函式中的作用 2.yield實現上...
python中迭代器的基本方法 Python迭代器
迭代器是可以迭代的物件。在本教程中,您將了解迭代器的工作原理,以及如何使用 iter 和 next 方法構建自己的迭代器。迭代器在python中無處不在。它們優雅地實現在迴圈,推導,生成器等中,但隱藏在明顯的視覺中。python中的迭代器只是乙個可以迭代的物件。乙個將一次返回資料的物件或乙個元素。從...