3 个 Python 3.2 功能在今天仍然适用

探索一些未被充分利用但仍然有用的 Python 功能。
47 位读者喜欢这个。
Business woman on laptop sitting in front of window

图片来自 Mapbox Uncharted ERG, CC-BY 3.0 US

这是关于 Python 3.x 版本中首次出现的功能系列的第三篇文章。其中一些 Python 版本已经发布一段时间了。 例如,Python 3.2 于 2011 年首次发布,但其中一些很酷且有用的功能仍然未被充分利用。 这里有三个。

argparse 子命令

argparse 模块首次出现在 Python 3.2 中。 有许多用于命令行解析的第三方模块。 但是内置的 argparse 模块比许多人认为的要强大得多。

记录 argparse 的所有来龙去脉将需要自己的系列文章。 为了让您尝鲜,这里有一个示例,说明如何使用 argparse 执行子命令。

想象一个带有两个子命令的命令:negate,它接受一个参数,以及 multiply,它接受两个参数

$ computebot negate 5
-5
$ computebot multiply 2 3
6
import argparse

parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers()

add_subparsers() 方法创建一个可以向其中添加子命令的对象。 唯一需要记住的技巧是,您需要通过 set_defaults() 添加调用的子命令

negate  = subparsers.add_parser("negate")
negate.set_defaults(subcommand="negate")
negate.add_argument("number", type=float)
multiply  = subparsers.add_parser("multiply")
multiply.set_defaults(subcommand="multiply")
multiply.add_argument("number1", type=float)
multiply.add_argument("number2", type=float)

我最喜欢的 argparse 功能之一是,因为它将解析与运行分离,所以测试解析逻辑特别令人愉快。

parser.parse_args(["negate", "5"])
    Namespace(number=5.0, subcommand='negate')
parser.parse_args(["multiply", "2", "3"])
    Namespace(number1=2.0, number2=3.0, subcommand='multiply')

contextlib.contextmanager

上下文是 Python 中的一个强大工具。 虽然许多人使用它们,但编写新的上下文通常看起来像是一门黑暗艺术。 使用 contextmanager 装饰器,您只需要一个一次性生成器。

编写一个上下文来打印出完成某事所花费的时间非常简单,如下所示

import contextlib, timeit

@contextlib.contextmanager
def timer():
    before = timeit.default_timer()
    try:
        yield
    finally:
        after = timeit.default_timer()
        print("took", after - before)

您可以像这样使用它

import time

with timer():
    time.sleep(10.5)
    took 10.511025413870811

functools.lru_cache

有时,将函数的结果缓存在内存中是有意义的。 例如,想象一下经典问题:“使用四分之一美元、一角硬币、五分镍币和美分,有多少种方法可以兑换一美元?”

此代码可能出奇地简单

def change_for_a_dollar():
    def change_for(amount, coins):
        if amount == 0:
            return 1
        if amount < 0 or len(coins) == 0:
            return 0
        some_coin = next(iter(coins))
        return (
            change_for(amount, coins - set([some_coin]))
            +
            change_for(amount - some_coin, coins)
        )
    return change_for(100, frozenset([25, 10, 5, 1]))

在我的电脑上,这大约需要 13 毫秒

with timer():
    change_for_a_dollar()
    took 0.013737603090703487

事实证明,当您计算出有多少种方法可以做类似用 50 美分兑换零钱的事情时,您会重复使用相同的硬币。 您可以使用 lru_cache 来避免一遍又一遍地重新计算。

import functools

def change_for_a_dollar():
    @functools.lru_cache
    def change_for(amount, coins):
        if amount == 0:
            return 1
        if amount < 0 or len(coins) == 0:
            return 0
        some_coin = next(iter(coins))
        return (
            change_for(amount, coins - set([some_coin]))
            +
            change_for(amount - some_coin, coins)
        )
    return change_for(100, frozenset([25, 10, 5, 1]))
with timer():
    change_for_a_dollar()
    took 0.004180959425866604

一行代码的成本带来了三倍的改进。 还不错。

欢迎来到 2011 年

尽管 Python 3.2 是 10 年前发布的,但它的许多功能仍然很酷且未被充分利用。 如果您还没有这样做,请将它们添加到您的工具包中。

接下来阅读什么
标签
Moshe sitting down, head slightly to the side. His t-shirt has Guardians of the Galaxy silhoutes against a background of sound visualization bars.
自 1998 年以来,Moshe 一直参与 Linux 社区,帮助举办 Linux “安装聚会”。 他自 1999 年以来一直编写 Python 程序,并为核心 Python 解释器做出了贡献。 Moshe 自这些术语出现之前就一直是 DevOps/SRE,非常关心软件可靠性、构建可重现性以及其他此类事情。

评论已关闭。

Creative Commons License本作品根据 Creative Commons Attribution-Share Alike 4.0 International License 获得许可。
© . All rights reserved.