Подборка @pythonetc, октябрь 2018

image

Это пятая подборка советов про Python и программирование из моего авторского канала @pythonetc.

Предыдущие подборки:


Разделитель --


Каждая порядочная утилита командной строки должна принимать аргументы в форме опций (например, -h или --help), опций с параметрами (--log-level 2) или позиционных параметров (cp file1 file2).

Опции отличаются от позиционных параметров наличием одной или двух черточек в начале. Когда с черточки начинаются позиционные аргументы, возникают проблемы: если вы хотите удалить файл -rf, команда rm -rf не сработает.

Простой способ выйти из положения — использовать в качестве разделителя две черточки. Аргументы после — не считаются опциями:

$ 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


Две черточки поддерживаются модулем argparse из коробки.

Устойчивость сортировки


Стандартная сортировка в Python — устойчивая, функция sorted не меняет порядок равных объектов:

In : a = [2, -1, 0, 1, -2]

In : sorted(a, key=lambda x: x**2)
Out: [0, -1, 1, 2, -2]


Функции min и max также согласованы с sorted. max работает как sorted (a, reverse=True)[0], а min — sorted (a)[0]. Это означает, что обе функции возвращают самый левый возможный ответ:

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


Кеш в аргументе по умолчанию


Пожалуй, самая распространенная ошибка среди начинающих питонистов — указание изменяемого объекта в качестве аргумента функции по умолчанию. Разделение этого объекта между вызовами функции может привести к самым странным результатам:

def append_length(lst=[]):
    lst.append(len(lst))
    return lst

print(append_length([1, 2])) # [1, 2, 2]
print(append_length())       # [0]
print(append_length())       # [0, 1]


Тем не менее, такой совместный доступ будет даже полезен, если использовать объект для создания общего кэша:

def fact(x, cache={0: 1}):
    if x not in cache:
        cache[x] = x * fact(x - 1)

    return cache[x]

print(fact(5))


В данном примере мы помещаем рассчитанные значения факториала внутрь значения аргумента по умолчанию. Такие значения даже можно извлечь:

>>> fact.__defaults__
({0: 1, 1: 1, 2: 2, 3: 6, 4: 24, 5: 120},)


Работа с ФС


Вы можете работать с путями файловой системы при помощи модуля os.path. Модуль содержит множество функций, которые воспринимают строки как файловые пути и проводят над ними разные полезные операции вроде конкатенации:

>>> import os.path
>>> os.path.join('/usr', 'local')
'/usr/local'
>>> os.path.dirname('/var/log')
'/var'


Начиная с версии 3.4, Python включает модуль pathlib, предлагающий объектно-ориентированный подход:

>>> 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'


Вызываемые объекты


В Python создать вызываемый объект можно не только создавая функции (с помощью синтаксических конструкций def или lambda). Объект становится вызываемым, если у него есть метод __call__:

class truncater:
    def __init__(self, length):
        self._length = length

    def __call__(self, s):
        return s[0:self._length]


print(truncater(4)('abcdabcd')) # abcd


Поскольку декоратор по сути является функцией высшего порядка, его также можно выразить вызываемым объектом, а не функцией:

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

© Habrahabr.ru