
This is the fifth collection of tips on Python and programming from my author’s @pythonetc channel.
Previous selections:
')
Separator --
Each decent command-line utility should take arguments in the form of options (for example,
-h or
--help ), options with parameters (
--log-level 2 ), or positional parameters (
cp file1 file2 ).
Options differ from positional parameters by having one or two dashes at the beginning. When positional arguments begin with a dash, problems arise: if you want to delete a file named
-rf , the
rm -rf command will not help you with this.
A simple way to get out of the situation is to use two dashes as a delimiter. Arguments after are not considered options:
$ echo test > -rf $ cat -rf cat: invalid option -- 'r' Try 'cat --help' for more information. $ cat -- -rf test $ rm -- -rf $ cat -- -rf cat: -rf: No such file or directory
Two dashes are supported by the argparse module out of the box.
Sorting stability
Standard sorting in Python is robust, the
sorted function
does not change the order of equal objects:
In : a = [2, -1, 0, 1, -2] In : sorted(a, key=lambda x: x**2) Out: [0, -1, 1, 2, -2]
The
min and
max functions are also consistent with
sorted. max works as
sorted (a, reverse = True) [0] , and
min - sorted (a) [0] . This means that both functions return the leftmost possible answer:
In : max([2, -2], key=lambda x: x**2) Out: 2 In : max([-2, 2], key=lambda x: x**2) Out: -2 In : min([2, -2], key=lambda x: x**2) Out: 2 In : min([-2, 2], key=lambda x: x**2) Out: -2
Default cache argument
Perhaps the most common mistake among novice Pythonists is specifying a variable object as the default argument of the function. The separation of this object between function calls may lead to the strangest results:
def append_length(lst=[]): lst.append(len(lst)) return lst print(append_length([1, 2]))
However, such sharing will even be useful if you use the object to create a shared cache:
def fact(x, cache={0: 1}): if x not in cache: cache[x] = x * fact(x - 1) return cache[x] print(fact(5))
In this example, we put the calculated factorial values inside the default argument value. Such values can even be extracted:
>>> fact.__defaults__ ({0: 1, 1: 1, 2: 2, 3: 6, 4: 24, 5: 120},)
Work with FS
You can work with file system paths using the
os.path module. The module contains many functions that perceive strings as file paths and perform various useful operations on them, such as concatenation:
>>> import os.path >>> os.path.join('/usr', 'local') '/usr/local' >>> os.path.dirname('/var/log') '/var'
Starting with version 3.4, Python includes the
pathlib module, offering an object-oriented approach:
>>> from pathlib import Path >>> Path('/usr') / Path('local') PosixPath('/usr/local') >>> Path('/usr') / 'local' PosixPath('/usr/local') >>> Path('/var/log').parent PosixPath('/var') >>> Path('/var/log').parent.name 'var'
Called Objects
In Python, you can create a callee not only by creating functions (using
def syntax or
lambda syntaxes). An object becomes invoked if it has a
__call__ method:
class Truncater: def __init__(self, length): self._length = length def __call__(self, s): return s[0:self._length] print(Truncater(4)('abcdabcd'))
Since the decorator is essentially a higher order function, it can also be expressed by the called object, and not by the function:
class cached: def __init__(self, func): self._func = func self._cache = {} def __call__(self, arg): if arg not in self._cache: self._cache[arg] = self._func(arg) return self._cache[arg] @cached def sqr(x): return x * x