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 argparseparser = 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 timewith 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 functoolsdef 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.