内函数﹝Python﹞

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)

5. 参考

  • Python Inner Functions—What Are They Good For?

你可能感兴趣的:(内函数﹝Python﹞)