發(fā)布于:2021-01-19 16:43:18
0
320
0
讓我們看看編寫內(nèi)部函數(shù)的三個常見原因。
注意:在Python中,函數(shù)是“一等公民”。這意味著它們與任何其他對象(整數(shù)、字符串、列表、模塊等)相當。您可以動態(tài)地創(chuàng)建或銷毀它們、將它們傳遞給其他函數(shù)、將它們作為值返回等等。
本教程使用Python 3.4.1版。
封裝
您使用內(nèi)部函數(shù)來保護它們不受函數(shù)外部發(fā)生的任何事情的影響,這意味著它們被隱藏在全局范圍之外。
這里有一個簡單的例子突出了這個概念:
def outer(num1):
def inner_increment(num1): # Hidden from outer code
return num1 + 1
num2 = inner_increment(num1)
print(num1, num2)
inner_increment(10)
# outer(10)
嘗試調用inner_increment()
:
Traceback (most recent call last):
File "inner.py", line 7, ininner_increment()
NameError: name 'inner_increment' is not defined
現(xiàn)在注釋掉inner_increment()
調用并取消外部函數(shù)調用的注釋,outer(10)
,作為參數(shù)傳入:
10 11
注意:請記住這只是一個示例。盡管這段代碼確實達到了預期的效果,但是最好使用前導下劃線將inner_increment()
變成頂級的“私有”函數(shù):_inner_increment()。
下面的遞歸示例是嵌套函數(shù)的一個稍微好一點的用例:
def factorial(number):
# Error handling
if not isinstance(number, int):
raise TypeError("Sorry. 'number' must be an integer.")
if not number >= 0:
raise ValueError("Sorry. 'number' must be zero or positive.")
def inner_factorial(number):
if number <= 1:
return 1
return number*inner_factorial(number-1)
return inner_factorial(number)
# Call the outer function.
print(factorial(4))
也測試一下。使用此設計模式的一個主要優(yōu)點是,通過在外部函數(shù)中執(zhí)行所有參數(shù)檢查,可以安全地跳過內(nèi)部函數(shù)中的錯誤檢查。
保持干燥
也許您有一個巨大的函數(shù),它在許多地方執(zhí)行相同的代碼塊。例如,您可能編寫了一個處理文件的函數(shù),并且希望接受打開的文件對象或文件名:
def process(file_name):
def do_stuff(file_process):
for line in file_process:
print(line)
if isinstance(file_name, str):
with open(file_name, 'r') as f:
do_stuff(f)
else:
do_stuff(file_name)
注意:同樣,通常只將do_stuff()
設為私有頂級函數(shù),但如果您希望將其作為內(nèi)部函數(shù)隱藏,則可以。一個實際的例子怎么樣?假設您想知道紐約市WiFi熱點的數(shù)量。是的,這個城市有原始數(shù)據(jù)告訴我們。訪問站點并下載CSV:
def process(file_name):
def do_stuff(file_process):
wifi_locations = {}
for line in file_process:
values = line.split(',')
# Build the dict and increment values.
wifi_locations[values[1]] = wifi_locations.get(values[1], 0) + 1
max_key = 0
for name, key in wifi_locations.items():
all_locations = sum(wifi_locations.values())
if key > max_key:
max_key = key
business = name
print(f'There are {all_locations} WiFi hotspots in NYC, '
f'and {business} has the most with {max_key}.')
if isinstance(file_name, str):
with open(file_name, 'r') as f:
do_stuff(f)
else:
do_stuff(file_name)
運行函數(shù):
>>> process('NAME_OF_THE.csv')
There are 1251 WiFi hotspots in NYC, and Starbucks has the most with 212.
閉包和工廠函數(shù)
現(xiàn)在我們來討論使用內(nèi)部函數(shù)的最重要原因。到目前為止,我們看到的所有內(nèi)部函數(shù)示例都是普通函數(shù),只是碰巧嵌套在另一個函數(shù)中。換句話說,我們可以用另一種方式定義這些函數(shù)(如前所述)。沒有具體的理由說明為什么需要嵌套它們。
但是當涉及到閉包時,情況并非如此:必須使用嵌套函數(shù)。
什么是閉包?
閉包只會使內(nèi)部函數(shù)在調用時記住其環(huán)境的狀態(tài)。初學者通常認為閉包是內(nèi)部函數(shù),但它實際上是由內(nèi)部函數(shù)引起的。閉包“關閉”堆棧上的局部變量,在堆棧創(chuàng)建完成后,這個問題仍然存在。
一個例子
這里有一個例子:
def generate_power(number):
"""
Examples of use:
>>> raise_two = generate_power(2)
>>> raise_three = generate_power(3)
>>> print(raise_two(7))
128
>>> print(raise_three(5))
243
"""
# Define the inner function ...
def nth_power(power):
return number ** power
# ... that is returned by the factory function.
return nth_power
示例中發(fā)生了什么
讓我們看看這個例子中發(fā)生了什么:
generate_power()是工廠函數(shù),僅表示每次調用它都會創(chuàng)建一個新函數(shù),然后返回新創(chuàng)建的函數(shù)。因此,raise_two()和raise_three()是新創(chuàng)建的功能。
這個新的內(nèi)部函數(shù)有什么作用?它只接受一個參數(shù)power,然后返回number**power。
內(nèi)部函數(shù)從哪里獲得價值number?這就是閉包起作用的地方:從外部函數(shù)(工廠函數(shù))nth_power()獲取值power。讓我們逐步完成此過程:
調用外部函數(shù):generate_power(2)。
Build nth_power(),它接受一個參數(shù)power。
拍攝的狀態(tài)快照nth_power(),其中包括number=2。
將該快照傳遞到中generate_power()。
返回nth_power()。
換句話說,閉包“初始化”其中的數(shù)字欄nth_power(),然后將其返回。現(xiàn)在,無論何時調用該新返回的函數(shù),它都將始終看到其自己的私有快照,其中包括number=2。
結論
閉包和工廠函數(shù)的使用是內(nèi)部函數(shù)的最常見和最強大的用法。在大多數(shù)情況下,當您看到修飾的函數(shù)時,修飾器是一個工廠函數(shù),它將一個函數(shù)作為參數(shù)并返回一個新函數(shù),該新函數(shù)在閉包內(nèi)部包括舊函數(shù)。停止。深吸一口氣。喝杯咖啡。再讀一遍。
換句話說,裝飾器只是用于實現(xiàn)generate_power()示例中概述的過程的語法糖。
我給您留下最后一個示例:
def generate_power(exponent):
def decorator(f):
def inner(*args):
result = f(*args)
return exponent**result
return inner
return decorator
@generate_power(2)
def raise_two(n):
return n
print(raise_two(7))
@generate_power(3)
def raise_three(n):
return n
print(raise_two(5))
如果您的代碼編輯器允許,并排查看generate_power(exponent)
和generate_power(number)
以說明所討論的概念。(例如,Sublime Text具有列視圖。)
如果尚未對這兩個函數(shù)進行編碼,請打開“代碼編輯器”并開始編碼。對于新程序員來說,編碼是一項實踐活動:就像騎自行車一樣,你只需要自己動手,自己動手。所以回到手頭的任務!
鍵入代碼后,您現(xiàn)在可以清楚地看到,它的相似之處在于產(chǎn)生相同的結果,但也存在差異。對于那些從未使用過decorators的人來說,如果你冒險沿著這條路走下去,注意這些差異將是理解它們的第一步。