Inner Functions
转载须注明出处:简书@Orca_J35 | GitHub@orca-j35,所有笔记均托管于 python_notes 仓库
1. 概述
内函数是指被嵌套在外层函数内部的函数,也称嵌套函数(Nested Functions)。例如:
def print_(): # print_ 是外函数
msg = "python"
def printer(): # printer是内函数
print(msg)
printer() # 调用内函数
print_() #> python
内层函数可以访问外层函数的局部变量(或参数)。比如,变量 msg
可以被内函数 printer
正常访问。即便存在多层嵌套,最内层的函数同样可以访问最外层函数的局部变量(或参数)。外层函数的局部变量(或参数)在内层函数中被称为非局部(nonlocal)变量,如果需要修改非局部变量,可使用 nonlocal
关键字。
def print_(): # print_ 是外函数
msg = "whale"
def printer(): # printer是内函数
nonlocal msg
msg = "dolpin"
print(msg)
printer() # 调用内函数
print_() #> dolpin
下面以两种内函数的使用场景为例,展示内函数的使用方法:
- 利用内函数可将操作封装(Encapsulation)到上层函数内部
- 过内函数整合重复出现的代码块
2. 封装
Encapsulation
内函数在上层函数之外不可见,在上层函数外部发生的任何变化都不会影响到内函数。因此,我们可以利用内函数将操作封装到上层函数的内部。在下面这个示例中,即使函数外部存在同名函数,也不会覆盖内函数:
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
下面这个递归函数进一步展示了内置函数的使用方式。这种设计模式的优点是将参数检查置于上层函数中,在内函数中无需考虑错误检测,单纯进行运算操作即可。
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
有时我们可能会在多个不同的地方使用相同的代码块。比如,当你编写一个用于处理文件的函数时,由于实参可以是文件对象或文件名,所以需要两个分支来应对不同的实参,但在这两个分支中必定会出现相同的代码块,此时便可通过内函数来整合重复出现的代码块。
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)
以上面的代码为基础,编写一个统计纽约市 WiFi 热点数量的函数(原始数据位于 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)