W3Cschool
恭喜您成為首批注冊(cè)用戶
獲得88經(jīng)驗(yàn)值獎(jiǎng)勵(lì)
你想要擴(kuò)展函數(shù)中的某個(gè)閉包,允許它能訪問和修改函數(shù)的內(nèi)部變量。
通常來講,閉包的內(nèi)部變量對(duì)于外界來講是完全隱藏的。但是,你可以通過編寫訪問函數(shù)并將其作為函數(shù)屬性綁定到閉包上來實(shí)現(xiàn)這個(gè)目的。例如:
def sample():
n = 0
# Closure function
def func():
print('n=', n)
# Accessor methods for n
def get_n():
return n
def set_n(value):
nonlocal n
n = value
# Attach as function attributes
func.get_n = get_n
func.set_n = set_n
return func
下面是使用的例子:
>>> f = sample()
>>> f()
n= 0
>>> f.set_n(10)
>>> f()
n= 10
>>> f.get_n()
10
>>>
為了說明清楚它如何工作的,有兩點(diǎn)需要解釋一下。首先,nonlocal
聲明可以讓我們編寫函數(shù)來修改內(nèi)部變量的值。其次,函數(shù)屬性允許我們用一種很簡單的方式將訪問方法綁定到閉包函數(shù)上,這個(gè)跟實(shí)例方法很像(盡管并沒有定義任何類)。
還可以進(jìn)一步的擴(kuò)展,讓閉包模擬類的實(shí)例。你要做的僅僅是復(fù)制上面的內(nèi)部函數(shù)到一個(gè)字典實(shí)例中并返回它即可。例如:
import sys
class ClosureInstance:
def __init__(self, locals=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()
下面是一個(gè)交互式會(huì)話來演示它是如何工作的:
>>> s = Stack()
>>> s
<__main__.ClosureInstance object at 0x10069ed10>
>>> s.push(10)
>>> s.push(20)
>>> s.push('Hello')
>>> len(s)
3
>>> s.pop()
'Hello'
>>> s.pop()
20
>>> s.pop()
10
>>>
有趣的是,這個(gè)代碼運(yùn)行起來會(huì)比一個(gè)普通的類定義要快很多。你可能會(huì)像下面這樣測試它跟一個(gè)類的性能對(duì)比:
class Stack2:
def __init__(self):
self.items = []
def push(self, item):
self.items.append(item)
def pop(self):
return self.items.pop()
def __len__(self):
return len(self.items)
如果這樣做,你會(huì)得到類似如下的結(jié)果:
>>> from timeit import timeit
>>> # Test involving closures
>>> s = Stack()
>>> timeit('s.push(1);s.pop()', 'from __main__ import s')
0.9874754269840196
>>> # Test involving a class
>>> s = Stack2()
>>> timeit('s.push(1);s.pop()', 'from __main__ import s')
1.0707052160287276
>>>
結(jié)果顯示,閉包的方案運(yùn)行起來要快大概8%,大部分原因是因?yàn)閷?duì)實(shí)例變量的簡化訪問,閉包更快是因?yàn)椴粫?huì)涉及到額外的self變量。
Raymond Hettinger對(duì)于這個(gè)問題設(shè)計(jì)出了更加難以理解的改進(jìn)方案。不過,你得考慮下是否真的需要在你代碼中這樣做,而且它只是真實(shí)類的一個(gè)奇怪的替換而已,例如,類的主要特性如繼承、屬性、描述器或類方法都是不能用的。并且你要做一些其他的工作才能讓一些特殊方法生效(比如上面 ClosureInstance
中重寫過的 __len__()
實(shí)現(xiàn)。)
最后,你可能還會(huì)讓其他閱讀你代碼的人感到疑惑,為什么它看起來不像一個(gè)普通的類定義呢?(當(dāng)然,他們也想知道為什么它運(yùn)行起來會(huì)更快)。盡管如此,這對(duì)于怎樣訪問閉包的內(nèi)部變量也不失為一個(gè)有趣的例子。
總體上講,在配置的時(shí)候給閉包添加方法會(huì)有更多的實(shí)用功能,比如你需要重置內(nèi)部狀態(tài)、刷新緩沖區(qū)、清除緩存或其他的反饋機(jī)制的時(shí)候。
Copyright©2021 w3cschool編程獅|閩ICP備15016281號(hào)-3|閩公網(wǎng)安備35020302033924號(hào)
違法和不良信息舉報(bào)電話:173-0602-2364|舉報(bào)郵箱:jubao@eeedong.com
掃描二維碼
下載編程獅App
編程獅公眾號(hào)
聯(lián)系方式:
更多建議: