3 个到今天仍然有用的 Python 3.2 特性,


探索一些未被充分利用但仍然有用的 Python 特性。

这是Python 3.x 首发特性系列文章中的第三篇。其中一些 Python 版本已经推出了一段时间。例如,Python 3.2 是在 2011 年首次发布的,但其中引入的一些很酷、很有用的特性仍然没有被使用。下面是其中的三个。

argparse 子命令

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

要记录所有的 argparse 的特性,那需要专门写系列文章。下面是一个例子,说明如何用 argparse 做子命令。

想象一下,一个命令有两个子命令:negate,需要一个参数,multiply,需要两个参数:

  1. $ computebot negate 5
  2. -5
  3. $ computebot multiply 2 3
  4. 6
  1. import argparse
  2.  
  3. parser = argparse.ArgumentParser()
  4. subparsers = parser.add_subparsers()

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

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

我最喜欢的一个 argparse 功能是,因为它把解析和运行分开,测试解析逻辑特别令人愉快。

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

contextlib.contextmanager

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

编写一个打印出做某事所需时间的上下文,就像这样简单:

  1. import contextlib, timeit
  2.  
  3. @contextlib.contextmanager
  4. def timer():
  5. before = timeit.default_timer()
  6. try:
  7. yield
  8. finally:
  9. after = timeit.default_timer()
  10. print("took", after - before)

你可以这样使用:

  1. import time
  2.  
  3. with timer():
  4. time.sleep(10.5)
  1. took 10.511025413870811`

functools.lru_cache

有时,在内存中缓存一个函数的结果是有意义的。例如,想象一下经典的问题:“有多少种方法可以用 25 美分、1 美分、2 美分和 3 美分可以来换取 1 美元?”

这个问题的代码可以说是非常简单:

  1. def change_for_a_dollar():
  2. def change_for(amount, coins):
  3. if amount == 0:
  4. return 1
  5. if amount < 0 or len(coins) == 0:
  6. return 0
  7. some_coin = next(iter(coins))
  8. return (
  9. change_for(amount, coins - set([some_coin]))
  10. +
  11. change_for(amount - some_coin, coins)
  12. )
  13. return change_for(100, frozenset([25, 10, 5, 1]))

在我的电脑上,这需要 13ms 左右:

  1. with timer():
  2. change_for_a_dollar()
  1. took 0.013737603090703487`

事实证明,当你计算有多少种方法可以做一些事情,比如用 50 美分找钱,你会重复使用相同的硬币。你可以使用 lru_cache 来避免重复计算。

  1. import functools
  2.  
  3. def change_for_a_dollar():
  4. @functools.lru_cache
  5. def change_for(amount, coins):
  6. if amount == 0:
  7. return 1
  8. if amount < 0 or len(coins) == 0:
  9. return 0
  10. some_coin = next(iter(coins))
  11. return (
  12. change_for(amount, coins - set([some_coin]))
  13. +
  14. change_for(amount - some_coin, coins)
  15. )
  16. return change_for(100, frozenset([25, 10, 5, 1]))
  1. with timer():
  2. change_for_a_dollar()
  1. took 0.004180959425866604`

一行的代价是三倍的改进。不错。

欢迎来到 2011 年

尽管 Python 3.2 是在 10 年前发布的,但它的许多特性仍然很酷,而且没有得到充分利用。如果你还没使用,那么将他们添加到你的工具箱中。

鸿蒙官方战略合作共建——HarmonyOS技术社区

评论关闭