python装饰器作用和功能_python装饰器大详解
一.作用域
在python中,作用域分為兩種:全局作用域和局部作用域。
全局作用域是定義在文件級別的變量,函數名。而局部作用域,則是定義函數內部。
關于作用域,我們要理解兩點:
a.在全局不能訪問到局部定義的變量
b.在局部能夠訪問到全局定義的變量,但是不能修改全局定義的變量(當然有方法可以修改)
下面我們來看看下面實例:
x = 1
deffunx():
x= 10
print(x) #打印出10
funx()print(x) #打印出1
如果局部沒有定義變量x,那么函數內部會從內往外開始查找x,如果沒有找到,就會報錯
x = 1
deffunx():print(x)
funx()print(x) #打印出1
x = 1
deffunx():deffunc1():print(x)
func1()
funx()print(x) #打印出1
因此,關于作用域的問題,只需要記住兩點就行:
全局變量能夠被文件任何地方引用,但修改只能在全局進行操作;如果局部沒有找到所需的變量,就會往外進行查找,沒有找到就會報錯。
二.高級函數
我們知道,函數名其實就是指向一段內存空間的地址,既然是地址,那么我們可以利用這種特性來。
a函數名可以作為一個值
defdelete(ps):importos
filename= ps[-1]
delelemetns= ps[1]
with open(filename, encoding='utf-8') as f_read,\
open('tmp.txt', 'w', encoding='utf-8') as f_write:for line in iter(f_read.readline, ''):if line != '\n': #處理非空行
if delelemetns inline:
line= line.replace(delelemetns,'')
f_write.write(line)
os.remove(filename)
os.rename('tmp.txt',filename)defadd(ps):
filename= ps[-1]
addelemetns= ps[1]
with open(filename,'a', encoding='utf-8') as fp:
fp.write("\n", addelemetns)defmodify(ps):importos
filename= ps[-1]
modify_elemetns= ps[1]
with open(filename, encoding='utf-8') as f_read, \
open('tmp.txt', 'w', encoding='utf-8') as f_write:for line in iter(f_read.readline, ''):if line != '\n': #處理非空行
if modify_elemetns inline:
line= line.replace(modify_elemetns, '')
f_write.write(line)
os.remove(filename)
os.rename('tmp.txt', filename)defsearch(cmd):
filename= cmd[-1]
pattern= cmd[1]
with open(filename,'r', encoding="utf-8") as f:for line inf:if pattern inline:print(line, end="")else:print("沒有找到")
dic_func={'delete': delete, 'add': add, 'modify': modify, 'search': search}whileTrue:
inp= input("請輸入您要進行的操作:").strip()if notinp:continuecmd_1=inp.split()
cmd=cmd_1[0]if cmd indic_func:
dic_func[cmd](cmd_1)else:print("Error")
將函數作為字典值,實現文本數據的增刪查改操作
b.函數名可以作為返回值
defouter():definner():pass
returninner
s=outer()print(s)######輸出結果為#######
.inner at 0x000000D22D8AB8C8>
c.函數名可以作為一個參數
defindex():print("index func")defouter(index):
s=index
s()
outer(index)######輸出結果#########
index func
所以滿足上面兩個條件中的一個,都可以稱為高級函數.
三.閉包函數
閉包函數必須滿足兩個條件:1.函數內部定義的函數 2.包含對外部作用域而非全局作用域的引用
下面通過一些實例來說明閉包函數:
實例一:以下僅僅在函數內部定義了一個函數,但并非閉包函數.
defouter():definner():print("inner func excuted")
inner()#調用執行inner()函數
print("outer func excuted")
outer()#調用執行outer函數
####輸出結果為##########
inner func excuted
outer func excuted
實例二:以下在函數內部定義了一個函數,而且還引用了一個外部變量x,那么這個是閉包函數么?答案:不是
x = 1
defouter():definner():print("x=%s" %x) #引用了一個非inner函數內部的變量
print("inner func excuted")
inner()#執行inner函數
print("outer func excuted")
outer()#####輸出結果########
x=1inner func excuted
outer func excuted
在回頭來看看對閉包函數的定義,是不是兩條都滿足?聰明的你,一定發現不滿足第二條.對,這里的變量x,是屬于全局變量,而非外部作用于域的變量。再來看看下面例子:
defouter():
x= 1
definner():print("x=%s" %x)print("inner func excuted")
inner()print("outer func excuted")
outer()#####輸出結果#########
x=1inner func excuted
outer func excuted
顯然,上面實例滿足閉包函數的條件。現在,你應該清楚,作為一個閉包函數,必須得滿足上述的兩個條件,缺一不可。但是,一般情況下,我們都會給閉包函數返回一個值.這里先不說為什么.在接下來的內容中,你會看到這個返回值的用途.
defouter():
x= 1
definner():print("x=%s" %x)print("inner func excuted")print("outer func excuted")return inner #返回內部函數名
outer()
現在我們來抽象的定義一下閉包函數。它是函數和與其相關的引用環境組合而成的實體。在實現深約束時,需要創建一個能顯式表示引用環境的東西,并將它與相關的子程序捆綁在一起,這樣捆綁起成為閉包。在上面實例中,我們可以發現,閉包函數,它必須包含自己的函數以及一個外部變量才能真正稱得上是一個閉包函數。如果沒有一個外部變量與其綁定,那么這個函數不能算得上是閉包函數。
那么怎么知道一個閉包函數有多少個外部引用變量呢?看看下面代碼.
defouter():
x= 1y= 2
definner():print("x= %s" %x)print("y= %s" %y)print(inner.__closure__)returninner
outer()######輸出結果#######
(, )
結果表明,在inner內部,引用了兩個外部局部變量。如果引用的是非局部變量,那么這里輸出的為None.
閉包函數的特點:1.自帶作用域 2.延遲計算
那么閉包函數有什么作用呢?我們清楚的知道,閉包函數在定義時,一定會綁定一個外部環境。這個整體才能算的上是一個閉包函數,那么我們可以利用這個綁定特性,來完成某些特殊的功能。
實例三:根據傳入的URL,來下載頁面源碼
from urllib.request importurlopendefindex(url)defget()returnurlopen(url).read()returnget
python= index("http://www.python.org") #返回的是get函數的地址
print(python()) #執行get函數《并且將返回的結果打印出來
baidu = index("http://www.baidu.com")print(baidu())
有人可以會說,這個不滿足閉包函數的條件啊!我沒有引用非全局的外部變量啊。其實并非如此,給,我們之前說過,只要在函數內部的變量都屬于函數。那么我在index(url),這個url也屬于函數內部,只不過我們省略一步而已,所以上面那個函數也是閉包函數。
四.裝飾器
有了以上基礎,對于裝飾器就好理解了.
裝飾器:外部函數傳入被裝飾函數名,內部函數返回裝飾函數名。
特點:1.不修改被裝飾函數的調用方式 2.不修改被裝飾函數的源代碼
a.無參裝飾器
有如下實例,我們需要計算一下代碼執行的時間。
importtime, randomdefindex():
time.sleep(random.randrange(1, 5))print("welcome to index page")
根據裝飾器的特點,我們不能對index()進行任何修改,而且調用方式也不能變。這時候,我們就可以使用裝飾器來完成如上功能.
importtime, randomdef outer(func): #將index的地址傳遞給func
definner():
start_time=time.time()
func()#fun = index 即func保存了外部index函數的地址
end_time =time.time()print("運行時間為%s"%(end_time -start_time))return inner #返回inner的地址
defindex():
time.sleep(random.randrange(1, 5))print("welcome to index page")
index= outer(index) #這里返回的是inner的地址,并重新賦值給index
index()
裝飾器實現計時
但是,有些情況,被裝飾的函數需要傳遞參數進去,有些函數又不需要參數,那么如何來處理這種變參數函數呢?下面來看看有參數裝飾器的使用情況.
b.有參裝飾器
def outer(func): #將index的地址傳遞給func
def inner(*args, **kwargs):
start_time=time.time()
func(*args, **kwargs) #fun = index 即func保存了外部index函數的地址
end_time =time.time()print("運行時間為%s"%(end_time -start_time))return inner #返回inner的地址
下面來說說一些其他情況的實例。
如果被裝飾的函數有返回值
deftimmer(func):def wrapper(*args,**kwargs):
start_time=time.time()
res=func(*args,**kwargs) #res來接收home函數的返回值
stop_time=time.time()print('run time is %s' %(stop_time-start_time))returnresreturnwrapperdefhome(name):
time.sleep(random.randrange(1,3))print('welecome to %s HOME page' %name)return 123123123123123123123123123123123123123123
這里補充一點,加入我們要執行被裝飾后的函數,那么應該是如下調用方式:
home = timmer(home) # 等式右邊返回的是wrapper的內存地址,再將其賦值給home,這里的home不在是原來的的那個函數,而是被裝飾以后的函數了。
像home = timmer(home)這樣的寫法,python給我們提供了一個便捷的方式------語法糖@.
以后我們再要在被裝飾的函數之前寫上@timmer,它的效果就和home = timmer(home)是一樣的。
如果一個函數被多個裝飾器裝飾,那么執行順序是怎樣的。
importtimeimportrandomdeftimmer(func):defwrapper():
start_time=time.time()
func()
stop_time=time.time()print('run time is %s' %(stop_time-start_time))returnwrapperdefauth(func):defdeco():
name=input('name:')
password=input('password:')if name == 'egon' and password == '123':print('login successful')
func()#wrapper()
else:print('login err')returndeco
@auth#index = auth(timmer(index))
@timmer #index = timmer(index)
defindex():
time.sleep(3)print('welecome to index page')
index()
View Code
實驗結果表明,多個裝飾器裝飾一個函數,其執行順序是從下往上。
關于裝飾器,還有一些高級用法,有興趣的可以自己研究研究。
c.類裝飾器
裝飾器不僅可以是函數,還可以是類,相比函數裝飾器,類裝飾器具有靈活度大、高內聚、封裝性等優點。使用類裝飾器主要依靠類的__call__方法,當使用 @ 形式將裝飾器附加到函數上時,就會調用此方法。
classFoo(object):def __init__(self, func):
self._func=funcdef __call__(self):print('class decorator runing')
self._func()print('class decorator ending')
@Foo # bar = Foo(bar)defbar():print('bar')
bar() # Foo(bar)()#結果#class decorator runing#bar#class decorator ending
classFoo(object):def __init__(self):pass
def __call__(self, func):def _call(*args, **kw):print('class decorator runing')return func(*args, **kw)return_callclassBar(object):
@Foo()def bar(self, test, ids): #bar = Foo()(bar)
print('bar')
Bar().bar('aa', 'ids')
總結
以上是生活随笔為你收集整理的python装饰器作用和功能_python装饰器大详解的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: javascript在第三个文本框中显示
- 下一篇: mysql call procedure