Курс по стандартной библиотеке: https://stepik.org/a/259466?utm_source=proproprogs
На этом занятии
речь пойдет о декораторе partial модуля functools стандартной
библиотеки Python. Что такое
декораторы функций, я думаю, вы хорошо себе представляете. Если это не так, то
декораторам было посвящены несколько занятий в базовом курсе языка Python. Напомню лишь,
что декораторы можно рассматривать, как некие обертки над существующими
функциями, которые несколько меняют их поведение (логику работы).
Итак, для чего
же предназначен декоратор partial? Давайте представим, что у нас в
программе объявлена следующая простая функция для вычисления периметра
треугольника:
import functools
def tr_per(a, b, c, add=0, mul=1): # периметр треугольника
print(a, b, c, add, mul)
return (a + b + c) * mul + add
Чтобы вызвать
эту функцию, ей необходимо передать три аргумента. Например:
Если при ее
вызове указать другое количество аргументов, то возникнет ошибка:
res = tr_per(5, 4) # ошибка
Но это поведение
можно изменить, если воспользоваться декоратором partial, который
позволяет фиксировать определенные параметры функций. Например, мы можем
применить его к функции tr_per следующим
образом:
rect_per = functools.partial(tr_per, 0) # фиксация a=0
Здесь 0
означает, что при вызове функции через ссылку rect_per в первый
параметр a будет автоматически
подставляться значение 0. В результате, вместо периметра треугольника будет
вычисляться сумма двух величин, которую можно интерпретировать, как половину
периметра прямоугольника:
res = rect_per(5, 4) # a=0, b=5, c=4
В
действительности, здесь создается новый объект-функция rect_per, которая
автоматически вызывает функцию tr_per с передачей ей
заданных значений аргументов.
Поправим
декорирование функции для вычисления полного периметра прямоугольника следующим
образом:
rect_per = functools.partial(tr_per, 0, mul=2) # фиксация a=0, mul=2
Здесь
именованный параметр mul=2 будет подставляться вместо прежнего mul=1, получим:
res = rect_per(5, 4) # a=0, b=5, c=4, add=0, mul=2
По аналогии мы
можем передать не одно, а сразу несколько значений:
rect_per2 = functools.partial(tr_per, 0, 1, mul=2) # фиксация a=0, b=1
или все три:
rect_per3 = functools.partial(tr_per, 0, 1, 2, mul=2, add=10)
Таким образом, с
помощью декоратора partial достаточно просто создавать специализированные
версии некоторых обобщенных функций. Например, если в программе объявляется универсальная
функция, принимающая много разных аргументов, то можно создать ее специализированные
версии с некоторыми наперед заданными значениями аргументов, сохранив при этом удобство
чтения и редактирования текста программы.
Декоратор partialmethod
Подобные
специализации можно делать не только на уровне отдельных функций, но и для
методов классов. Только в этом случае следует применять другой декоратор partialmethod. Давайте
посмотрим на его работу на конкретном примере.
Допустим,
требуется описать класс для работы с БД с методом connect() для
установления соединения:
class DataBase:
def __init__(self):
self.host = self.port = self.shema = None
def connect(self, host, port, shema=None):
self.host = host
self.port = port
self.shema = shema
print(f"Connect: {self.host}:{self.port} - {self.shema}")
Этот метод connect() можно
специализировать, указав значение параметр shema с некоторым
стандартным значением. Такую специализацию удобно реализовать с помощью
декоратора partialmethod следующим
образом:
class DataBase:
...
default_connect = functools.partialmethod(connect, shema='default-shema')
В результате, в
классе DataBase появляется еще
один атрибут default_connect, через который
можно вызывать специализированный метод connect():
db = DataBase()
db.default_connect("mydb", 1234)
Возможно, здесь
у вас возникает вопрос, зачем понадобился отдельный декоратор partialmethod для методов
класса? Почему бы не использовать ранее рассмотренный декоратор partial? На самом деле,
мы можем воспользоваться и таким декоратором:
class DataBase:
...
default_connect = functools.partial(connect, shema='default-shema')
Но тогда при
вызове метода default_connect() придется
дополнительно первым аргументом передавать объект, для которого вызывается этот
метод:
db = DataBase()
db.default_connect(db, "mydb", 1234)
То есть, отличие
декоратора partialmethod от partial заключается в автоматической
передаче значения для первого параметра self, который
присутствует у всех обычных методов класса.
Таким образом,
декоратор partialmethod часто применяется для повышения удобства работы с
методами классов, когда стандартный интерфейс оказывается избыточным или
неудобным. Чаще всего partialmethod используют для сокращения кода, минимизации
ошибок и облегчения понимания общего дизайна классов.
Курс по стандартной библиотеке: https://stepik.org/a/259466?utm_source=proproprogs