Python 嵌套函数

作者: 一去、二三里
个人微信号: iwaleon
微信公众号: 高效程序员

在 Python 中,函数的用法可谓是千变万化,只不过我们平日接触的大多是一些基本用法。函数强大之处当然不止于此,它还有很多高级用法 - 闭包、装饰器。

这些高级用法都与嵌套函数紧密相关,所以有必要先熟悉一下嵌套函数。

嵌套函数

嵌套函数(Nested function)是在另一个函数(即:封闭函数)中定义的函数

嵌套函数的基本概念在前面已经介绍过了,参见:Python 函数的高级用法

那么,一般在什么情况下使用嵌套函数?

  • 封装 - 数据隐藏
  • 贯彻 DRY 原则
  • 闭包

除此之外,嵌套函数还有其他用法,只不过这些是比较常用的。另外,闭包的内容较为丰富,后面做单独分享。

封装 - 数据隐藏

可以使用内层函数来保护它们不受函数外部变化的影响,也就是说把它们从全局作用域隐藏起来。

来看一个简单的例子 - 求一个数字 n 的倍数:

>>> def outer(n):
...     def inner_multiple(n):  # 从外部代码隐藏
...         return n * 2
...     num = inner_multiple(n)
...     print(num)
... 
>>>

尝试调用 inner_multiple(n) 会引发错误:

>>> outer(5)
10
>>> 
>>> inner_multiple(5)  # 外部无法访问
...
NameError: name 'inner_multiple' is not defined

这是因为它被定义在 outer() 的内部,被隐藏了起来,所以外部无法访问。

Python 递归函数 一节中,分享过一个关于阶乘的递归实现,但是存在一个问题 - 没有做容错处理。

下面来利用嵌套函数,对递归做一个更好的实现:

>>> def factorial(n):
...
...     # 错误处理
...     if not isinstance(n, int):  # 仅限整形
...         raise TypeError("Sorry. 'n' must be an integer.")
...     if n < 0:  # 仅限 0 和 正数
...         raise ValueError("Sorry. 'n' must be zero or positive.")
...     
...     def inner_factorial(n):
...         if n <= 1:
...             return 1
...         return n * inner_factorial(n - 1)
...     return inner_factorial(n)
... 
>>> 

这样以来,程序就会变得更加健壮,当数据类型或值不合理时,便会引发错误:

>>> factorial('Py')  # 类型错误
...
TypeError: Sorry. 'n' must be an integer.
>>> 
>>> factorial(-10)  # 值错误
...
ValueError: Sorry. 'n' must be zero or positive.
>>> 
>>> factorial(5)  # OK
120

使用这种模式的主要优势在于:利用外部函数执行所有的参数检查,便可以在内部函数中跳过错误检查,并安全放心地进行使用。

贯彻 DRY 原则

DRY(Don’t Repeat Yourself)- 是指在程序设计以及计算中避免重复代码,因为这样会降低灵活性、简洁性,并且有可能导致代码之间的矛盾。

DRY 更多的是一种架构设计思想,在软件开发过程中的万事万物均可能重复,大到标准、框架、开发流程;中到组件、接口;小到功能、代码均纯存在自我重复。而 DRY 提倡的就是在软件开发过程中应消除所有这些自我重复。

如果看过《程序员修炼之道》,想必对这一思想有所了解,那里面做了很好的阐述。

例如,处理文件时,需要支持打开的文件对象和文件名,传统方式是采用两个函数分别实现。

支持文件对象:

>>> def print_file(f):
...     for line in f:
...         print(line)
... 
>>> f = open('data.txt', 'r')
>>> print_file(f) 
Hi, all

I am a Pythonista

>>> 

支持文件名:

>>> def print_file_content(file_name):
...     if isinstance(file_name, str):
...         with open(file_name, 'r') as f:
...             for line in f:
...                 print(line)
... 
>>> print_file_content('data.txt')
Hi, all

I am a Pythonista

>>> 

很显然,有很多代码可以复用,通过嵌套函数来实现:

>>> def print_file(file):
...     def inner_print(file_process):
...         for line in file_process:
...             print(line, end = '')
...     
...     if isinstance(file, str):
...         with open(file, 'r') as f:
...             inner_print(f)
...     else:
...         inner_print(file)
... 
>>>

这样,一个函数便可以支持多种方式,简洁很重要。

>>> f = open('data.txt', 'r')
>>> print_file(f)
Hi, all
I am a Pythonista
>>> 
>>> print_file('data.txt')
Hi, all
I am a Pythonista

除此之外,通常也会把 inner_print() 作为一个顶层私有函数,但是若想要将其作为一个内层函数隐藏起来,就用嵌套函数。

  • 33
    点赞
  • 79
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一去丶二三里

有收获,再打赏!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值