В библиотеке functools представлен мощный инструмент partial с одним из вариантов использования которого я хочу разобраться.
functools.partial возвращает partial объект, который содержит 3 ридонли атрибута: func (объект или функцию), args (позиционные аргументы), keywords (именованный аргументы); при вызове, этот объект будет вести себя, как func, вызываемый с позиционными аргументами и именованными.
functools.partial можно использоваться для создания новых производных функций, которым предварительно установлены некоторые входные параметры.
Простой пример, в коде нужно отфильтровать числа меньше чем, например, где-то 10, а где-то 0.
Можно сделать так:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
def less_than_zero(total): return total < 0 def less_than_ten(total): return total < 10 to_filter_one = [2, 24, 39, 60, 34, 23, 64, 8, 89, 11] to_filter_second = [47, 75, 30, 25, 65, 69, -2, 26, 44, 0] print(list(filter(less_than_ten, to_filter_one))) # [2, 8] print(list(filter(less_than_zero, to_filter_second))) # [-2] |
Обстоятельства меняются и в коде приходится сталкиваться, например, с фильтром по значениям, которые устанавливает пользователь; и здесь нам нужно преобразовать код так, что бы не загромождать его и передать необходимое для фильтрации значение, поможет в этом functools.partial
1 2 3 4 5 6 7 8 9 10 11 12 13 |
from functools import partial def less_than(total, min_val): return total < min_val to_filter_one = [2, 24, 39, 60, 34, 23, 64, 8, 89, 11] to_filter_second = [47, 75, 30, 25, 65, 69, -2, 26, 44, 0] print(list(filter(partial(less_than, min_val=24), to_filter_one))) # [2, 23, 8, 11] print(list(filter(partial(less_than, min_val=5), to_filter_second))) # [-2, 0] |
С помощью partial мы передаем значения min_val, получается, что код мы переиспользуем тот же, особого загромождения не происходит; работает это как с именованными, так и с позиционными аргументами. При этом метаданные функции сохраняются и мы можем их посмотреть.
1 2 3 |
result = partial(less_than, min_val=1) print(result.func) # <function less_than at 0x7fbbe4dc6ea0> |
Пример, с позиционными аргументами
1 2 3 4 5 6 7 8 9 10 11 |
import re from functools import partial text = ''' Lorem ipsum dolor sit amet, consectetur adipiscing elit (122) 234-9993. Aliquam quis laoreet dolor. Praesent euismod sapien orci, eu fringilla urna eleifend non. Nulla tincidunt, diam ut posuere viverra, (122)655-0098 leo mi eleifend mi, at ultricies dolor nibh a tortor. ''' is_spaced_apart = partial(re.findall, "[(][\d]{3}[)][ ]?[\d]{3}-[\d]{4}") print(is_spaced_apart(text)) # ['(122) 234-9993', '(122)655-0098'] |
functools.partial позволяет удалить избыточный код и повысить возможность его повторного использования.