W3Cschool
恭喜您成為首批注冊用戶
獲得88經(jīng)驗值獎勵
你想要擴展函數(shù)中的某個閉包,允許它能訪問和修改函數(shù)的內(nèi)部變量。
通常來講,閉包的內(nèi)部變量對于外界來講是完全隱藏的。但是,你可以通過編寫訪問函數(shù)并將其作為函數(shù)屬性綁定到閉包上來實現(xiàn)這個目的。例如:
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
>>>
為了說明清楚它如何工作的,有兩點需要解釋一下。首先,nonlocal
聲明可以讓我們編寫函數(shù)來修改內(nèi)部變量的值。其次,函數(shù)屬性允許我們用一種很簡單的方式將訪問方法綁定到閉包函數(shù)上,這個跟實例方法很像(盡管并沒有定義任何類)。
還可以進一步的擴展,讓閉包模擬類的實例。你要做的僅僅是復制上面的內(nèi)部函數(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()
下面是一個交互式會話來演示它是如何工作的:
>>> 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
>>>
有趣的是,這個代碼運行起來會比一個普通的類定義要快很多。你可能會像下面這樣測試它跟一個類的性能對比:
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)
如果這樣做,你會得到類似如下的結果:
>>> 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
>>>
結果顯示,閉包的方案運行起來要快大概8%,大部分原因是因為對實例變量的簡化訪問,閉包更快是因為不會涉及到額外的self變量。
Raymond Hettinger對于這個問題設計出了更加難以理解的改進方案。不過,你得考慮下是否真的需要在你代碼中這樣做,而且它只是真實類的一個奇怪的替換而已,例如,類的主要特性如繼承、屬性、描述器或類方法都是不能用的。并且你要做一些其他的工作才能讓一些特殊方法生效(比如上面 ClosureInstance
中重寫過的 __len__()
實現(xiàn)。)
最后,你可能還會讓其他閱讀你代碼的人感到疑惑,為什么它看起來不像一個普通的類定義呢?(當然,他們也想知道為什么它運行起來會更快)。盡管如此,這對于怎樣訪問閉包的內(nèi)部變量也不失為一個有趣的例子。
總體上講,在配置的時候給閉包添加方法會有更多的實用功能,比如你需要重置內(nèi)部狀態(tài)、刷新緩沖區(qū)、清除緩存或其他的反饋機制的時候。
Copyright©2021 w3cschool編程獅|閩ICP備15016281號-3|閩公網(wǎng)安備35020302033924號
違法和不良信息舉報電話:173-0602-2364|舉報郵箱:jubao@eeedong.com
掃描二維碼
下載編程獅App
編程獅公眾號
聯(lián)系方式:
更多建議: