Python閉包技巧介紹
1.閉包:用函數(shù)代替類(lèi)
有時(shí)我們會(huì)定義只有一個(gè)方法(除了__init__()之外)的類(lèi),而這種類(lèi)可以通過(guò)使用閉包(closure)來(lái)替代。閉包是被外層函數(shù)包圍的內(nèi)層函數(shù),它能夠獲取外層函數(shù)范圍中的變量(即使外層函數(shù)已執(zhí)行完畢)。因此閉包可以保存額外的變量環(huán)境,用于在函數(shù)調(diào)用時(shí)使用??紤]下面這個(gè)例子,這個(gè)類(lèi)允許用戶(hù)通過(guò)某種模板方案來(lái)獲取URL。
from urllib.request import urlopen
class UrlTemplate:
def __init__(self, template) -> None:
self.template = template
def open(self, **kwargs):
return urlopen(self.template.format_map(kwargs))
yahoo = UrlTemplate('http://finance.yahoo.com/d/quotes.csv?s={names}&f={fields}')
for line in yahoo.open(names='IBM,AAPL,FB', fields = 'sllclv'):
print(line.decode('utf-8'))
這個(gè)類(lèi)可以用一個(gè)簡(jiǎn)單的函數(shù)來(lái)替代:
def urltempalte(template):
# 后面用對(duì)象再次調(diào)用函數(shù)的時(shí)候傳入kwargs
def opener(**kwargs):
return urlopen(template.format_map(kwargs))
return opener
yahoo = urltempalte('http://finance.yahoo.com/d/quotes.csv?s={names}&f={fields}')
for line in yahoo(names='IBM,AAPL,FB', fields = 'sllclv'):
print(line.decode('utf-8'))
在許多情況下,我們會(huì)使用只有單個(gè)方法的類(lèi)的原因是需要保存額外的狀態(tài)給類(lèi)方法使用。我們上面提到的UrlTemplate類(lèi)的唯一目的就是將template的值保存在某處,然后在open()方法中使用它。而使用閉包來(lái)解決該問(wèn)題會(huì)更加簡(jiǎn)短和優(yōu)雅,我們使用opener()函數(shù)記住參數(shù)template的值,然后再隨后的調(diào)用中使用該值。
所以大家在編寫(xiě)代碼中需要附加額外的狀態(tài)給函數(shù)時(shí),一定要考慮使用閉包。
2.訪問(wèn)定義在閉包的內(nèi)的變量
我們知道閉包內(nèi)層可以用來(lái)存儲(chǔ)函數(shù)需要用到的變量環(huán)境。接下來(lái),我們可以通過(guò)函數(shù)來(lái)擴(kuò)展閉包,使得在閉包內(nèi)層定義的變量可以被訪問(wèn)和修改。
一般來(lái)說(shuō),閉包內(nèi)層定義的變量對(duì)外界來(lái)說(shuō)是完全隔離的,如果想要訪問(wèn)和修改它們,需要編寫(xiě)存取函數(shù)(accessor function, 即getter/setter方法),并將它們做為函數(shù)屬性附加到閉包上來(lái)提供對(duì)內(nèi)層變量的訪問(wèn)支持:
def sample():
n = 0
# 閉包函數(shù)
def func():
print("n =", n)
# 存取函數(shù)(accessor function),即getter/setter方法
def get_n():
return n
def set_n(value):
# 必須要加nolocal才能修改內(nèi)層變量
nonlocal n
n = value
# 做為函數(shù)屬性附加
func.get_n = get_n
func.set_n = set_n
return func
該算法測(cè)試運(yùn)行結(jié)果如下:
f = sample() f() # n = 0 f.set_n(10) f() # n = 10 print(f.get_n()) # 10
可以看到,get_n()和set_n()工作起來(lái)很像實(shí)例的方法。注意一定要將get_n()和set_n()做為函數(shù)屬性附加上去,否則在調(diào)用set_n()和get_n()就會(huì)報(bào)錯(cuò):'function' object has no attribute 'set_n'。
如果我們希望讓閉包完全模擬成類(lèi)實(shí)例,我們需要架構(gòu)內(nèi)層函數(shù)拷貝到一個(gè)實(shí)例的字典中然后將它返回。
示例如下:
import sys
class ClosureInstance:
def __init__(self, locals=None) -> None:
if locals is None:
locals = sys._getframe(1).f_locals
# Update instance dictionary with callables
self.__dict__.update(
(key, value) for key, value in locals.items() if callable(value)
)
# Redirect special methods
def __len__(self):
return self.__dict__['__len__']()
# Example use
def Stack():
items = []
def push(item):
items.append(item)
def pop():
return items.pop()
def __len__():
return len(items)
return ClosureInstance()
下面展示了對(duì)應(yīng)的測(cè)試結(jié)果:
s = Stack()
print(s) # <__main__.ClosureInstance object at 0x101efc280>
s.push(10)
s.push(20)
s.push('Hello')
print(len(s)) # 3
print(s.pop()) # Hello
print(s.pop()) # 20
print(s.pop()) # 10
用閉包模型類(lèi)的功能比傳統(tǒng)的類(lèi)實(shí)現(xiàn)方法要快一些。比如我們用下面這個(gè)類(lèi)做為測(cè)試對(duì)比。
class Stack2:
def __init__(self) -> None:
self.items = []
def push(self, item):
self.items.append(item)
def pop(self):
return self.items.pop()
def __len__(self):
return len(self.items)
下面是我們的測(cè)試結(jié)果:
from timeit import timeit
s = Stack()
print(timeit('s.push(1);s.pop()', 'from __main__ import s'))
# 0.98746542
s = Stack2()
print(timeit('s.push(1);s.pop()', 'from __main__ import s'))
# 1.07070521
可以看到采用閉包的版本要快大約8%。因?yàn)閷?duì)于實(shí)例而言,測(cè)試話費(fèi)的大部分時(shí)間都在對(duì)實(shí)例變量的訪問(wèn)上,而閉包要更快一些,因?yàn)椴挥蒙婕邦~外的self變量。
不過(guò)這種奇技淫巧在代碼中還是要謹(jǐn)慎使用,因?yàn)橄啾纫粋€(gè)真正的類(lèi),這種方法其實(shí)是相當(dāng)怪異的。像繼承、屬性、描述符或者類(lèi)方法這樣的特性在這種方法都是無(wú)法使用的。而且我們還需要一些花招才能讓特殊方法正常工作(比如我們上面ClosureInstance中對(duì)__len__()的實(shí)現(xiàn))。不過(guò),這仍然是一個(gè)非常有學(xué)術(shù)價(jià)值的例子,它告訴我們對(duì)閉包內(nèi)部提供訪問(wèn)機(jī)制能夠?qū)崿F(xiàn)怎樣的功能。
到此這篇關(guān)于Python閉包技巧介紹的文章就介紹到這了,更多相關(guān)Python閉包內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
python工具dtreeviz決策樹(shù)可視化和模型可解釋性
這篇文章主要介紹了python工具dtreeviz決策樹(shù)可視化和模型可解釋性,決策樹(shù)是梯度提升機(jī)和隨機(jī)森林的基本構(gòu)建塊,在學(xué)習(xí)這些模型的工作原理和模型可解釋性時(shí),可視化決策樹(shù)是一個(gè)非常有幫助,下文相關(guān)資料,需要的小伙伴可任意參考一下2022-03-03
Python測(cè)試網(wǎng)絡(luò)連通性示例【基于ping】
這篇文章主要介紹了Python測(cè)試網(wǎng)絡(luò)連通性,結(jié)合實(shí)例形式分析了Python通過(guò)發(fā)送ping請(qǐng)求測(cè)試網(wǎng)絡(luò)連通性相關(guān)操作技巧,需要的朋友可以參考下2018-08-08
TensorFlow實(shí)現(xiàn)checkpoint文件轉(zhuǎn)換為pb文件
今天小編就為大家分享一篇TensorFlow實(shí)現(xiàn)checkpoint文件轉(zhuǎn)換為pb文件,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-02-02
Tensorflow 實(shí)現(xiàn)線性回歸模型的示例代碼
這篇文章主要介紹了Tensorflow 實(shí)現(xiàn)線性回歸模型,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-05-05
Python實(shí)現(xiàn)常見(jiàn)的4種坐標(biāo)互相轉(zhuǎn)換
主流被使用的地理坐標(biāo)系并不統(tǒng)一,常用的有WGS84、GCJ02(火星坐標(biāo)系)、BD09(百度坐標(biāo)系)以及百度地圖中保存矢量信息的web墨卡托,本文利用Python編寫(xiě)相關(guān)類(lèi)以實(shí)現(xiàn)4種坐標(biāo)系統(tǒng)之間的互相轉(zhuǎn)換,希望對(duì)大家有所幫助2023-02-02
python?針對(duì)在子文件夾中的md文檔實(shí)現(xiàn)批量md轉(zhuǎn)word
這篇文章主要介紹了python?針對(duì)在子文件夾中的md文檔實(shí)現(xiàn)批量md轉(zhuǎn)word,但是自己保存的md文檔在不同的文件夾,而大部分只能實(shí)現(xiàn)同一文件夾內(nèi)的轉(zhuǎn)換,得出下列總結(jié),需要的朋友可以參考一下2022-04-04
numpy取反操作符和Boolean類(lèi)型與0-1表示方式
這篇文章主要介紹了numpy取反操作符和Boolean類(lèi)型與0-1表示方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-03-03
python鏈接Oracle數(shù)據(jù)庫(kù)的方法
這篇文章主要介紹了python鏈接Oracle數(shù)據(jù)庫(kù)的方法,實(shí)例分析了Python使用cx_Oracle模塊操作Oracle數(shù)據(jù)庫(kù)的相關(guān)技巧,需要的朋友可以參考下2015-06-06
200行python代碼實(shí)現(xiàn)2048游戲
這篇文章主要為大家詳細(xì)介紹了200行Python代碼實(shí)現(xiàn)2048游戲,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-07-07

