Science and technology

three Python three.2 options which might be nonetheless related in the present day

This the third article in a sequence about options that first appeared in a model of Python three.x. Some of these Python variations have been out for some time. For instance, Python three.2 was first launched in 2011, but a few of the cool and helpful options launched in it are nonetheless underused. Here are three of them.

argparse subcommands

The argparse module first appeared in Python three.2. There are many third-party modules for command-line parsing. But the built-in argparse module is extra highly effective than many give it credit score for.

Documenting all of the ins and outs of argparse would take its personal article sequence. For a small style, right here is an instance of how you are able to 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 three
6

import argparse

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

The add_subparsers() strategies creates an object which you could add subcommands to. The solely trick to recollect is that you could add what subcommand was referred to as by means of a set_defaults():

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

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

One of my favourite argparse options is that, as a result of it separates parsing from operating, testing the parsing logic is especially nice.

parser.parse_args(["negate", "5"])
    Namespace(quantity=5.zero, subcommand='negate')
parser.parse_args(["multiply", "2", "3"])
    Namespace(number1=2.zero, number2=three.zero, subcommand='multiply')

contextlib.contextmanager

Contexts are a strong device in Python. While many use them, writing a brand new context usually looks like a darkish artwork. With the contextmanager decorator, all you want is a one-shot generator.

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

import contextlib, timeit

@contextlib.contextmanager
def timer():
    earlier than = timeit.default_timer()
    attempt:
        yield
    lastly:
        after = timeit.default_timer()
        print("took", after - earlier than)

And you need to use it with simply:

import time

with timer():
    time.sleep(10.5)

    took 10.511025413870811

Sometimes the caching outcomes from a perform in reminiscence make sense. For instance, think about the classical downside: “How many ways can you make change for a dollar with quarters, dimes, nickels, and cents?”

The code for this may be deceptively easy:

def change_for_a_dollar():
    def change_for(quantity, cash):
        if quantity == zero:
            return 1
        if quantity < zero or len(cash) == zero:
            return zero
        some_coin = subsequent(iter(cash))
        return (
            change_for(quantity, cash - set([some_coin]))
            +
            change_for(quantity - some_coin, cash)
        )
    return change_for(100, frozenset([25, 10, 5, 1]))

On my pc, this takes round 13ms:

with timer():
    change_for_a_dollar()

    took zero.013737603090703487

It seems that if you calculate what number of methods you are able to do one thing like making change from 50 cents, you employ the identical cash repeatedly. You can use lru_cache to keep away from recalculating this time and again.

import functools

def change_for_a_dollar():
    @functools.lru_cache
    def change_for(quantity, cash):
        if quantity == zero:
            return 1
        if quantity < zero or len(cash) == zero:
            return zero
        some_coin = subsequent(iter(cash))
        return (
            change_for(quantity, cash - set([some_coin]))
            +
            change_for(quantity - some_coin, cash)
        )
    return change_for(100, frozenset([25, 10, 5, 1]))

with timer():
    change_for_a_dollar()

    took zero.004180959425866604

A 3-fold enchancment for the price of one line. Not unhealthy.

Welcome to 2011

Although Python three.2 was launched 10 years in the past, a lot of its options are nonetheless cool—and underused. Add them to your toolkit if you have not already.

Most Popular

To Top