當前位置:
首頁 > 知識 > python裝飾器大詳解

python裝飾器大詳解

1.作用域

在python中,作用域分為兩種:全局作用域和局部作用域。

全局作用域是定義在文件級別的變數,函數名。而局部作用域,則是定義函數內部。

關於作用域,我要理解兩點:a.在全局不能訪問到局部定義的變數 b.在局部能夠訪問到全局定義的變數,但是不能修改全局定義的變數(當然有方法可以修改)

下面我們來看看下面實例:

x = 1
def funx:
x = 10
print(x) # 列印出10

funx
print(x) # 列印出1

如果局部沒有定義變數x,那麼函數內部會從內往外開始查找x,如果沒有找到,就會報錯

x = 1
def funx:
print(x) # 列印出1

funx
print(x) # 列印出1

x = 1
def funx:
def func1:
print(x) # 列印出1
func1

funx
print(x) # 列印出1

因此,關於作用域的問題,只需要記住兩點就行:全局變數能夠被文件任何地方引用,但修改只能在全局進行操作;如果局部沒有找到所需的變數,就會往外進行查找,沒有找到就會報錯。

2.高級函數

我們知道,函數名其實就是指向一段內存空間的地址,既然是地址,那麼我們可以利用這種特性來。

a函數名可以作為一個值

def delete(ps):
import os
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 != "
": # 處理非空行
if delelemetns in line:
line = line.replace(delelemetns,"")
f_write.write(line)
os.remove(filename)
os.rename("tmp.txt",filename)

def add(ps):
filename = ps[-1]
addelemetns = ps[1]
with open(filename, "a", encoding="utf-8") as fp:
fp.write("
", addelemetns)

def modify(ps):
import os
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 != "
": # 處理非空行
if modify_elemetns in line:
line = line.replace(modify_elemetns, "")
f_write.write(line)
os.remove(filename)
os.rename("tmp.txt", filename)

def search(cmd):
filename = cmd[-1]
pattern = cmd[1]
with open(filename, "r", encoding="utf-8") as f:
for line in f:
if pattern in line:
print(line, end="")
else:
print("沒有找到")

dic_func ={"delete": delete, "add": add, "modify": modify, "search": search}

while True:
inp = input("請輸入您要進行的操作:").strip
if not inp:
continue
cmd_1 = inp.split
cmd = cmd_1[0]
if cmd in dic_func:
dic_func[cmd](cmd_1)
else:
print("Error")

將函數作為字典值,實現文本數據的增刪查改操作

b.函數名可以作為返回值

def outer:
def inner:
pass
return inner

s = outer
print(s)

######輸出結果為#######
.inner at 0x000000D22D8AB8C8>

c..函數名可以作為一個參數

def index:
print("index func")

def outer(index):
s = index
s

outer(index)

######輸出結果#########

index func

所以滿足上面兩個條件中的一個,都可以稱為高級函數.

3.閉包函數

閉包函數必須滿足兩個條件:1.函數內部定義的函數 2.包含對外部作用域而非全局作用域的引用

下面通過一些實例來說明閉包函數:

實例一:以下僅僅在函數內部定義了一個函數,但並非閉包函數.

def outer:
def inner:
print("inner func excuted")
inner # 調用執行inner函數
print("outer func excuted")
outer # 調用執行outer函數

####輸出結果為##########
inner func excuted
outer func excuted

實例二:以下在函數內部定義了一個函數,而且還引用了一個外部變數x,那麼這個是閉包函數么?答案:不是

x = 1
def outer:

def inner:
print("x=%s" %x) # 引用了一個非inner函數內部的變數
print("inner func excuted")
inner # 執行inner函數
print("outer func excuted")

outer
#####輸出結果########
x=1
inner func excuted
outer func excuted

在回頭來看看對閉包函數的定義,是不是兩條都滿足?聰明的你,一定發現不滿足第二條.對,這裡的變數x,是屬於全局變數,而非外部作用於域的變數。再來看看下面例子:

def outer:
x = 1
def inner:
print("x=%s" %x)
print("inner func excuted")
inner
print("outer func excuted")

outer

#####輸出結果#########
x=1
inner func excuted
outer func excuted

顯然,上面實例滿足閉包函數的條件。現在,你應該清楚,作為一個閉包函數,必須得滿足上述的兩個條件,缺一不可。但是,一般情況下,我們都會給閉包函數返回一個值.這裡先不說為什麼.在接下來的內容中,你會看到這個返回值的用途.

def outer:
x = 1
def inner:
print("x=%s" %x)
print("inner func excuted")
print("outer func excuted")
return inner # 返回內部函數名

outer

現在我們來抽象的定義一下閉包函數。它是函數和與其相關的引用環境組合而成的實體。在實現深約束時,需要創建一個能顯式表示引用環境的東西,並將它與相關的子程序捆綁在一起,這樣捆綁起成為閉包。在上面實例中,我們可以發現,閉包函數,它必須包含自己的函數以及一個外部變數才能真正稱得上是一個閉包函數。如果沒有一個外部變數與其綁定,那麼這個函數不能算得上是閉包函數。

那麼怎麼知道一個閉包函數有多少個外部引用變數呢?看看下面代碼.

def outer:
x = 1
y = 2

def inner:
print("x= %s" %x)
print("y= %s" %y)

print(inner.__closure__)
return inner

outer

######輸出結果#######
(, )

結果表明,在inner內部,引用了兩個外部局部變數。如果引用的是非局部變數,那麼這裡輸出的為None.

閉包函數的特點:1.自帶作用域 2.延遲計算

那麼閉包函數有什麼作用呢?我們清楚的知道,閉包函數在定義時,一定會綁定一個外部環境。這個整體才能算的上是一個閉包函數,那麼我們可以利用這個綁定特性,來完成某些特殊的功能。

實例三:根據傳入的URL,來下載頁面源碼

from urllib.request import urlopen

def index(url)
def get
return urlopen(url).read
return get

python = index("http://www.python.org") # 返回的是get函數的地址
print(python) # 執行get函數《並且將返回的結果列印出來
baidu = index("http://www.baidu.com")
print(baidu)

有人可以會說,這個不滿足閉包函數的條件啊!我沒有引用非全局的外部變數啊。其實並非如此,給,我們之前說過,只要在函數內部的變數都屬於函數。那麼我在index(url),這個url也屬於函數內部,只不過我們省略一步而已,所以上面那個函數也是閉包函數。

4.裝飾器

有了以上基礎,對於裝飾器就好理解了.

裝飾器:外部函數傳入被裝飾函數名,內部函數返回裝飾函數名。

特點:1.不修改被裝飾函數的調用方式 2.不修改被裝飾函數的源代碼

a.無參裝飾器

有如下實例,我們需要計算一下代碼執行的時間。

import time, random

def index:
time.sleep(random.randrange(1, 5))
print("welcome to index page")

根據裝飾器的特點,我們不能對index進行任何修改,而且調用方式也不能變。這時候,我們就可以使用裝飾器來完成如上功能.

import time, random

def outer(func): # 將index的地址傳遞給func
def inner:
start_time = time.time
func # fun = index 即func保存了外部index函數的地址
end_time = time.time
print("運行時間為%s"%(end_time - start_time))
return inner # 返回inner的地址

def index:
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的地址

下面來說說一些其他情況的實例。

如果被裝飾的函數有返回值

def timmer(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))
return res
return wrapper

def home(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)是一樣的。

如果一個函數被多個裝飾器裝飾,那麼執行順序是怎樣的。

import time
import random

def timmer(func):
def wrapper:
start_time = time.time
func
stop_time=time.time
print("run time is %s" %(stop_time-start_time))
return wrapper
def auth(func):
def deco:
name=input("name: ")
password=input("password: ")
if name == "egon" and password == "123":
print("login successful")
func #wrapper
else:
print("login err")
return deco

@auth # index = auth(timmer(index))
@timmer # index = timmer(index)
def index:

time.sleep(3)
print("welecome to index page")

index

View Code

實驗結果表明,多個裝飾器裝飾一個函數,其執行順序是從下往上。

關於裝飾器,還有一些高級用法,有興趣的可以自己研究研究。

喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 達人科技 的精彩文章:

Java虛擬機14:Java對象大小、對象內存布局及鎖狀態變化
用JMX遠程監控Tomcat
google snapy 壓縮文件
Scrapy教程——博客園前3000名文章列表抓取
Vulkan Tutorial 14 Integration pipeline

TAG:達人科技 |

您可能感興趣

Python super 詳解
MyBatis 配置 typeHandlers 詳解
詳解Pony最新妝容-Flower that booms at night
Tensorboard 詳解
pentestdb 架構詳解
Python Excel處理庫openpyxl詳解
stringr包詳解
乾貨詳解 Linux phantomJs install course
怎麼用Photoshop摳圖?詳解
SpringBoot | 第三章:springboot 配置詳解
OpenStack之Magnum容器編服務排引擎詳解
詳解TogetherJS
eBay運營之best match規則詳解
聚合查詢慢——詳解Global Ordinals與High Cardinality
詳解Elasticsearch的基友Logstash
redis.conf配置文件詳解
Linux的chown和chmod授權詳解
基於Python Selenium Unittest PO設計模式詳解
HashMap詳解
Shopify費用包括哪些?Shopify費用詳解