TranslateProject/sources/tech/20210514 3 Python 3.2 features that are still relevant today.md
2021-05-17 08:51:16 +08:00

5.6 KiB

3 Python 3.2 features that are still relevant today

Explore some of the underutilized but still useful Python features. Business woman on laptop sitting in front of window

This the third article in a series about features that first appeared in a version of Python 3.x. Some of those Python versions have been out for a while. For example, Python 3.2 was first released in 2011, yet some of the cool and useful features introduced in it are still underused. Here are three of them.

argparse subcommands

The argparse module first appeared in Python 3.2. There are many third-party modules for command-line parsing. But the built-in argparse module is more powerful than many give it credit for.

Documenting all the ins and outs of argparse would take its own article series. For a small taste, here is an example of how you can do subcommands with argparse.

Imagine a command with two subcommands: negate, which takes one argument, and multiply which takes two:

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

[/code] [code]

import argparse

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

The add_subparsers() methods creates an object that you can add subcommands to. The only trick to remember is that you need to add what subcommand was called through a set_defaults():

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

[/code] [code]

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

One of my favorite argparse features is that, because it separates parsing from running, testing the parsing logic is particularly pleasant.

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

contextlib.contextmanager

Contexts are a powerful tool in Python. While many use them, writing a new context often seems like a dark art. With the contextmanager decorator, all you need is a one-shot generator.

Writing a context that prints out the time it took to do something is as simple as:

import contextlib, timeit

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

And you can use it with just:

import time

with timer():
    time.sleep(10.5)

[/code] [code]`    took 10.511025413870811`

functools.lru_cache

Sometimes the caching results from a function in memory make sense. For example, imagine the classical problem: "How many ways can you make change for a dollar with quarters, dimes, nickels, and cents?"

The code for this can be deceptively simple:

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]))

On my computer, this takes around 13ms:

with timer():
    change_for_a_dollar()

[/code] [code]`    took 0.013737603090703487`

It turns out that when you calculate how many ways you can do something like making change from 50 cents, you use the same coins repeatedly. You can use lru_cache to avoid recalculating this over and over.

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]))

[/code] [code]

with timer():
    change_for_a_dollar()

[/code] [code]`    took 0.004180959425866604`

A three-fold improvement for the cost of one line. Not bad.

Welcome to 2011

Although Python 3.2 was released 10 years ago, many of its features are still cool—and underused. Add them to your toolkit if you haven't already.


via: https://opensource.com/article/21/5/python-32

作者:Moshe Zadka 选题:lujun9972 译者:译者ID 校对:校对者ID

本文由 LCTT 原创编译,Linux中国 荣誉推出