乐趣区

关于python:翻译实用的Python编程0703Returningfunctions

目录 | 上一节 (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 4
7

局部变量

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

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

退出移动版