百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 编程字典 > 正文

Python内部函数——用处何在

toyiye 2024-06-21 12:26 9 浏览 0 评论

这是一篇译文,原文地址:https://realpython.com/inner-functions-what-are-they-good-for/


1. 封装

内部函数可以免受函数之外的情况的影响,也就是说,对于全局命名空间而言,它们是隐藏的。

下面是一个简单的例子:

def outer(num1): def inner_increment(num1): # 对外部空间隐藏 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, in <module> inner_increment()NameError: name 'inner_increment' is not defined

注释掉对 inner_increment() 的直接调用,对外部的函数传入参数 10,即 outer(10) 是可以运行的:

10 11

注意:这只是一个例子,虽然这些代码可以运作,但就这个函数而言,可能更好的方式是把 inner_increment() 定义为存在于外部空间的“私有”函数,即在函数名前加一个下划线前缀,即 _inner_increment() 。

下面这个嵌套函数可能是一个更好的使用内部函数的例子:

def factorial(number):
 # 处理错误
 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) 

# 调用外部函数
print(factorial(4))

在这里,我们把参数验证放在外部函数,而在内部函数中处理关键步骤。


2. 避免自我重复(DRY原则)

有时,我们可能会在一个大型函数中,重复地使用一些代码。比方说,我们写一个处理文件的函数,同时支持文件名或文件对象作为参数:

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() 放在外部,作为一个私有函数,但显然,必要时我们也可以把它作为内部函数隐藏起来。

我们可以写一个更具体的例子。

假如说,我们想了解纽约市的 WIFI 热点数据,可以直接在网上下载对应的 CSV 文件,然后进行统计:

def process(file_name):
 def do_stuff(file_process):
 wifi_locations = {}
 for line in file_process:
 values = line.split(',') # 创建一个字典,记录统计数据 
 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'纽约市总共有 {all_locations} 个 WIFI 热点,'
 f'{business} 提供的热点最多,有 {max_key} 个。')
 
 if isinstance(file_name, str):
 with open(file_name, 'r') as f:
 do_stuff(f)
 else:
 do_stuff(file_name)

运行后得到结果如下:

>>> process('NAME_OF_THE.csv')
纽约市总共有 1251 个 WiFi 热点,Starbucks 提供的热点最多,有 212 个。

3. 闭包与工厂函数

接下来我们要讨论的是使用内部函数最重要的理由。在之前的例子中,内部函数都是一个常规函数,只是恰好被嵌套在另一个函数中而已。也就是说,我们完全用其它方式定义它们(如之前已经提示的),并非一定要使用内部函数。

而在考虑闭包的时候,我们就必须使用嵌套函数了。

什么是闭包

闭包可以使内部函数记住它所在空间的具体状态。新手们常常以为内部函数就是闭包,准确地说,应该是内部函数制造了闭包。所谓闭包,所“封闭”的是函数帧中的局部变量。

一个例子

以下是一个例子:

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
 """
 # 定义内部函数
 def nth_power(power):
 return number ** power
 
 # 将函数作为外部函数的结果返回
 return nth_power

对例子的解释

让我们看看这个例子中具体发生了什么:

  1. generate_power() 是一个工厂函数,每次调用它时,会返回一个新创建的函数,因此,raise_two、raise_three 指向的是这些新创建的函数;
  2. 这些新创建的函数,需要一个参数 power,返回的值是 number**power;
  3. 那么,这个 number 的值是怎么来的呢?这就是闭包发生作用的地方:nth_power() 函数是从外部函数,即工厂函数获取 number 的值的。整个过程可以分解步骤如下:
  • 调用外部函数:generate_power(2);
  • 创建函数 nth_power(),它需要一个参数 power;
  • 保存 nth_power() 函数帧的状态,其中包括 number=2;
  • 将保存的函数帧状态传递给 generate_power() 函数;
  • 返回 nth_power() 函数;

换句话说,闭包为 nth_power() 函数提供了初始化数据并将它返回。因此,我们调用这个被返回的函数时,总是可以在其函数帧中找到 number=2。

一个实际应用

现在,让我们考虑一个真实世界中的例子:

def has_permission(page):
 def inner(username):
 if username == 'Admin':
 return "'{0}' does have access to {1}.".format(username, page)
 else:
 return "'{0}' does NOT have access to {1}.".format(username, page)
 return inner

current_user = has_permission('Admin Area')
print(current_user('Admin'))

random_user = has_permission('Admin Area')
print(random_user('Not Admin'))

这是一个简化版的权限判断函数,我们也可以做简单修改,从 session 中获取用户信息,进而判断这个用户是否具有接入某个路由的权限。显然,我们会从数据库中查询用户权限,而不是检查用户名是否等于 'Admin'。


总结

闭包与函数工厂是内部函数最常见、最主要的用处。大多数情况下,如果你看到一个带装饰器的函数,这个装饰器就是一个函数工厂,它以一个函数作为参数,并返回一个新的函数,新的函数使用闭包包括了作为参数的函数。

换句话说,装饰器就是一个语法糖,它的基本流程其实和上面所举的 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 中的分栏功能)

如果你还没写出这两个函数的话,建议还是亲自在编辑器中敲出来一次,对编程新手来说,写代码就行骑自行车:你必须亲自上手。

敲出这些代码后,你就能看出,它们产生了类似的结果,但也有一些不同。对于还没有用过装饰器的人来说,注意到这些不同,就是理解它们的开始。


END

公众号:ReadingPython

相关推荐

为何越来越多的编程语言使用JSON(为什么编程)

JSON是JavascriptObjectNotation的缩写,意思是Javascript对象表示法,是一种易于人类阅读和对编程友好的文本数据传递方法,是JavaScript语言规范定义的一个子...

何时在数据库中使用 JSON(数据库用json格式存储)

在本文中,您将了解何时应考虑将JSON数据类型添加到表中以及何时应避免使用它们。每天?分享?最新?软件?开发?,Devops,敏捷?,测试?以及?项目?管理?最新?,最热门?的?文章?,每天?花?...

MySQL 从零开始:05 数据类型(mysql数据类型有哪些,并举例)

前面的讲解中已经接触到了表的创建,表的创建是对字段的声明,比如:上述语句声明了字段的名称、类型、所占空间、默认值和是否可以为空等信息。其中的int、varchar、char和decimal都...

JSON对象花样进阶(json格式对象)

一、引言在现代Web开发中,JSON(JavaScriptObjectNotation)已经成为数据交换的标准格式。无论是从前端向后端发送数据,还是从后端接收数据,JSON都是不可或缺的一部分。...

深入理解 JSON 和 Form-data(json和formdata提交区别)

在讨论现代网络开发与API设计的语境下,理解客户端和服务器间如何有效且可靠地交换数据变得尤为关键。这里,特别值得关注的是两种主流数据格式:...

JSON 语法(json 语法 priority)

JSON语法是JavaScript语法的子集。JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔花括号保存对象方括号保存数组JS...

JSON语法详解(json的语法规则)

JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔大括号保存对象中括号保存数组注意:json的key是字符串,且必须是双引号,不能是单引号...

MySQL JSON数据类型操作(mysql的json)

概述mysql自5.7.8版本开始,就支持了json结构的数据存储和查询,这表明了mysql也在不断的学习和增加nosql数据库的有点。但mysql毕竟是关系型数据库,在处理json这种非结构化的数据...

JSON的数据模式(json数据格式示例)

像XML模式一样,JSON数据格式也有Schema,这是一个基于JSON格式的规范。JSON模式也以JSON格式编写。它用于验证JSON数据。JSON模式示例以下代码显示了基本的JSON模式。{"...

前端学习——JSON格式详解(后端json格式)

JSON(JavaScriptObjectNotation)是一种轻量级的数据交换格式。易于人阅读和编写。同时也易于机器解析和生成。它基于JavaScriptProgrammingLa...

什么是 JSON:详解 JSON 及其优势(什么叫json)

现在程序员还有谁不知道JSON吗?无论对于前端还是后端,JSON都是一种常见的数据格式。那么JSON到底是什么呢?JSON的定义...

PostgreSQL JSON 类型:处理结构化数据

PostgreSQL提供JSON类型,以存储结构化数据。JSON是一种开放的数据格式,可用于存储各种类型的值。什么是JSON类型?JSON类型表示JSON(JavaScriptO...

JavaScript:JSON、三种包装类(javascript 包)

JOSN:我们希望可以将一个对象在不同的语言中进行传递,以达到通信的目的,最佳方式就是将一个对象转换为字符串的形式JSON(JavaScriptObjectNotation)-JS的对象表示法...

Python数据分析 只要1分钟 教你玩转JSON 全程干货

Json简介:Json,全名JavaScriptObjectNotation,JSON(JavaScriptObjectNotation(记号、标记))是一种轻量级的数据交换格式。它基于J...

比较一下JSON与XML两种数据格式?(json和xml哪个好)

JSON(JavaScriptObjectNotation)和XML(eXtensibleMarkupLanguage)是在日常开发中比较常用的两种数据格式,它们主要的作用就是用来进行数据的传...

取消回复欢迎 发表评论:

请填写验证码