简介
本节介绍使用函数来创建其他函数的概念。
This tutorial is from open-source community. Access the source code
💡 本教程由 AI 辅助翻译自英文原版。如需查看原文,您可以 切换至英文原版
本节介绍使用函数来创建其他函数的概念。
考虑以下函数。
def add(x, y):
def do_add():
print('Adding', x, y)
return x + y
return do_add
这是一个返回另一个函数的函数。
>>> a = add(3,4)
>>> a
<function add.<locals>.do_add at 0x7f27d8a38790>
>>> a()
Adding 3 4
7
观察内部函数如何引用外部函数定义的变量。
def add(x, y):
def do_add():
## `x` 和 `y` 在 `add(x, y)` 之上定义
print('Adding', x, y)
return x + y
return do_add
进一步观察,在 add()
完成后,这些变量不知为何仍然存在。
>>> a = add(3,4)
>>> a
<function do_add at 0x6a670>
>>> a()
Adding 3 4 ## 这些值从哪里来?
7
当一个内部函数作为结果被返回时,该内部函数被称为闭包。
def add(x, y):
## `do_add` 是一个闭包
def do_add():
print('Adding', x, y)
return x + y
return do_add
基本特性:闭包会保留函数稍后正常运行所需的所有变量的值。可以将闭包看作是一个函数加上一个额外的环境,该环境保存了它所依赖的变量的值。
闭包是Python的一个重要特性。然而,它们的使用往往很微妙。常见应用包括:
考虑这样一个函数:
def after(seconds, func):
import time
time.sleep(seconds)
func()
用法示例:
def greeting():
print('Hello Guido')
after(30, greeting)
after
会在稍后执行提供的函数。
闭包会携带额外的信息。
def add(x, y):
def do_add():
print(f'Adding {x} + {y} -> {x+y}')
return do_add
def after(seconds, func):
import time
time.sleep(seconds)
func()
after(30, add(2, 3))
## `do_add` 具有对 x -> 2 和 y -> 3 的引用
闭包还可以用作避免过度代码重复的一种技术。你可以编写生成代码的函数。
闭包更强大的特性之一是其在生成重复代码方面的应用。如果你回顾一下练习5.7,还记得用于定义带类型检查的属性的代码。
class Stock:
def __init__(self, name, shares, price):
self.name = name
self.shares = shares
self.price = price
...
@property
def shares(self):
return self._shares
@shares.setter
def shares(self, value):
if not isinstance(value, int):
raise TypeError('Expected int')
self._shares = value
...
你无需一遍又一遍地重复输入这段代码,而是可以使用闭包自动创建它。
创建一个名为typedproperty.py
的文件,并将以下代码放入其中:
## typedproperty.py
def typedproperty(name, expected_type):
private_name = '_' + name
@property
def prop(self):
return getattr(self, private_name)
@prop.setter
def prop(self, value):
if not isinstance(value, expected_type):
raise TypeError(f'Expected {expected_type}')
setattr(self, private_name, value)
return prop
现在,通过定义如下类来试用一下:
from typedproperty import typedproperty
class Stock:
name = typedproperty('name', str)
shares = typedproperty('shares', int)
price = typedproperty('price', float)
def __init__(self, name, shares, price):
self.name = name
self.shares = shares
self.price = price
尝试创建一个实例并验证类型检查是否有效。
>>> s = Stock('IBM', 50, 91.1)
>>> s.name
'IBM'
>>> s.shares = '100'
... 应该会得到一个TypeError...
>>>
在上述示例中,用户可能会觉得像typedproperty('shares', int)
这样的调用输入起来有点冗长 —— 尤其是如果它们被大量重复使用时。在typedproperty.py
文件中添加以下定义:
String = lambda name: typedproperty(name, str)
Integer = lambda name: typedproperty(name, int)
Float = lambda name: typedproperty(name, float)
现在,重写Stock
类以使用这些函数:
class Stock:
name = String('name')
shares = Integer('shares')
price = Float('price')
def __init__(self, name, shares, price):
self.name = name
self.shares = shares
self.price = price
啊,这样好多了。这里的主要收获是闭包和lambda
通常可以用来简化代码并消除烦人的重复。这通常是件好事。
恭喜你!你已经完成了“返回函数”实验。你可以在LabEx中练习更多实验来提升你的技能。