写点什么

翻译:《实用的 Python 编程》07_03_Returning_functions

用户头像
codists
关注
发布于: 2021 年 03 月 22 日
翻译:《实用的Python编程》07_03_Returning_functions

目录 | [上一节 (7.2 匿名函数)] | [下一节 (7.4 装饰器)]


7.3 返回函数


本节介绍使用函数创建其它函数的思想。


简介


考虑以下函数:


def add(x, y):    def do_add():        print('Adding', x, y)        return x + y    return do_add
复制代码


这是返回其它函数的函数。


>>> a = add(3,4)>>> a<function do_add at 0x6a670>>>> a()Adding 3 47
复制代码


局部变量


请观察内部函数是如何引用外部函数定义的变量的。


def add(x, y):    def do_add():        # `x` and `y` are defined above `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      # Where are these values coming from?7
复制代码


闭包


当内部函数作为结果返回时,该内部函数称为闭包(closure)。


def add(x, y):    # `do_add` is a closure    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 (延迟 30 秒后)执行给定的函数......


闭包附带了其它信息。


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` has the references x -> 2 and y -> 3
复制代码


代码重复


闭包也可以用作一种避免代码大量重复的技术。


练习


练习 7.7:使用闭包避免重复


闭包的一个更强大的特性是用于生成重复的代码。让我们回顾 练习 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'... should get a TypeError ...>>>
复制代码


练习 7.8:简化函数调用


在上面示例中,用户可能会发现调用诸如 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 常用于简化代码,并消除令人讨厌的代码重复。这通常很不错。


练习 7.9:付诸实践


请重新编写 stock.py 文件中的 Stock 类,以便使用上面展示的类型化特性(typed properties)。


目录 | [上一节 (7.2 匿名函数)] | [下一节 (7.4 装饰器)]


注:完整翻译见 https://github.com/codists/practical-python-zh


发布于: 2021 年 03 月 22 日阅读数: 14
用户头像

codists

关注

公众号:编程人 2021.01.14 加入

Life is short, You need Python

评论

发布
暂无评论
翻译:《实用的Python编程》07_03_Returning_functions