Классы в Python можно вызывать как функции. Да, те самые обычные две скобки () при создании экземпляра.
В этот момент вызывается "магический метод" `__call__` 🧙♂️. По умолчанию он вызывает ошибку TypeError, если применяется к экземпляру класса без реализации.
Классы, которые так себя ведут, называют "функторы" 🧠. Странное название, ну да ладно 😁.
Но зачем это надо? 🤔 Давайте разберемся на примерах.
1) Простой счетчик
class Counter:
def __init__(self):
self.__counter = 0
def __call__(self, *args, **kwargs):
print('__call__')
self.__counter += 1
return self.__counter
c = Counter()
c()
c()
res = c()
print(res) # 3 🎯
2) Счетчик с параметром:
Можем немножко изменить код, чтобы туда можно было передавать аргументы, Что выведется в последней строчке тогда, как думаете?
class Counter:
def __init__(self):
self.__counter = 0
def __call__(self, step=1, *args, **kwargs):
print('__call__')
self.__counter += step
return self.__counter
c = Counter()
c2 = Counter()
c()
c(10)
res = c(3)
res2 = c2(-5)
print(res, res2) # что выведет? 🧠
Еще пару вариантов:
3) Замена замыканий функций:
+ в чат если помните что это такое 🙃. Пример для обработки строки через strip(). Но можно придумать что угодно другое.
class StripChars:
def __init__(self, chars):
# символы для удаления в конце и начале строки
self.__chars = chars
def __call__(self, *args, **kwargs):
if not isinstance(args[0], str):
raise ValueError("Аргумент должен быть строкой")
return args[0].strip(self.__chars)
s1 = StripChars("?:!.; ")
res = s1("; Hello World! ")
print(res) # "Hello World" ✨
4) Реализация декораторов, с помощью классов.
Математика вошла в чат 📊. Здесь вычисляется значение производной функции в конкретной точке х.
import math
class Derivate:
def __init__(self, func):
self.__fn = func
def __call__(self, x, dx=0.0001, *args, **kwargs):
return (self.__fn(x + dx) - self.__fn(x)) / dx
@Derivate
def df_sin(x):
return math.sin(x)
# df_sin = Derivate(df_sin) # в "лоб"
print(df_sin(math.pi/3)) # производная синуса 🧮
Итог: Функторы — это мощный инструмент, когда нужно сохранять состояние между вызовами или создавать умные декораторы! 🚀
А вы используете функторы в своих проектах? Делитесь в комментариях! 👇
P.S. Кто угадал результат во втором примере? 😄