Inner Functions
轉(zhuǎn)載須注明出處:簡書@Orca_J35 | GitHub@orca-j35,所有筆記均托管于 python_notes 倉庫
1. 概述
內(nèi)函數(shù)是指被嵌套在外層函數(shù)內(nèi)部的函數(shù),也稱嵌套函數(shù)(Nested Functions)。例如:
def print_(): # print_ 是外函數(shù)
msg = "python"
def printer(): # printer是內(nèi)函數(shù)
print(msg)
printer() # 調(diào)用內(nèi)函數(shù)
print_() #> python
內(nèi)層函數(shù)可以訪問外層函數(shù)的局部變量(或參數(shù))。比如,變量 msg 可以被內(nèi)函數(shù) printer 正常訪問。即便存在多層嵌套,最內(nèi)層的函數(shù)同樣可以訪問最外層函數(shù)的局部變量(或參數(shù))。外層函數(shù)的局部變量(或參數(shù))在內(nèi)層函數(shù)中被稱為非局部(nonlocal)變量,如果需要修改非局部變量,可使用 nonlocal 關(guān)鍵字。
def print_(): # print_ 是外函數(shù)
msg = "whale"
def printer(): # printer是內(nèi)函數(shù)
nonlocal msg
msg = "dolpin"
print(msg)
printer() # 調(diào)用內(nèi)函數(shù)
print_() #> dolpin
下面以兩種內(nèi)函數(shù)的使用場景為例,展示內(nèi)函數(shù)的使用方法:
- 利用內(nèi)函數(shù)可將操作封裝(Encapsulation)到上層函數(shù)內(nèi)部
- 過內(nèi)函數(shù)整合重復出現(xiàn)的代碼塊
2. 封裝
Encapsulation
內(nèi)函數(shù)在上層函數(shù)之外不可見,在上層函數(shù)外部發(fā)生的任何變化都不會影響到內(nèi)函數(shù)。因此,我們可以利用內(nèi)函數(shù)將操作封裝到上層函數(shù)的內(nèi)部。在下面這個示例中,即使函數(shù)外部存在同名函數(shù),也不會覆蓋內(nèi)函數(shù):
def outer(num1):
def _inner_increment(num1): # Hidden from outer code
return num1 + 1
num2 = _inner_increment(num1)
print(num1, num2)
def _inner_increment(num1):
return num1 + 99
outer(10) #> 10 11
下面這個遞歸函數(shù)進一步展示了內(nèi)置函數(shù)的使用方式。這種設計模式的優(yōu)點是將參數(shù)檢查置于上層函數(shù)中,在內(nèi)函數(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)) #> 24
3. DRY
Don't repeat yourself
有時我們可能會在多個不同的地方使用相同的代碼塊。比如,當你編寫一個用于處理文件的函數(shù)時,由于實參可以是文件對象或文件名,所以需要兩個分支來應對不同的實參,但在這兩個分支中必定會出現(xiàn)相同的代碼塊,此時便可通過內(nèi)函數(shù)來整合重復出現(xiàn)的代碼塊。
def process(file_name):
def _do_stuff(file_process): # 通過嵌套函數(shù)整合重復出現(xiàn)的代碼塊
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)
以上面的代碼為基礎,編寫一個統(tǒng)計紐約市 WiFi 熱點數(shù)量的函數(shù)(原始數(shù)據(jù)位于 NYC Wi-Fi Hotspot Locations,請使用 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.
# values[1] is Borough
wifi_locations[values[1]] = wifi_locations.get(values[1], 0) + 1
all_locations = sum(wifi_locations.values())
max_key = 0
for name, key in wifi_locations.items():
if key > max_key:
max_key = key
name_of_Borough = name
print(f'There are {all_locations} WiFi hotspots in NYC, '
f'and {name_of_Borough} has the most with {max_key}.')
if isinstance(file_name, str):
with open(file_name, 'r', encoding="utf_8") as f:
_do_stuff(f)
else:
_do_stuff(file_name)
process("NYC_Wi-Fi_Hotspot_Locations.csv")
#> There are 6411 WiFi hotspots in NYC, and MN has the most with 1603.
4. 擴展閱讀
- Information hiding - Wikipedia
- Thinking Recursively in Python
- Don't repeat yourself - Wikipedia
- Factory (object-oriented programming)