Как сделать так, чтобы именно класс мог быть декоратором, а не его экземпляры?

Подскажите, пожалуйста, как сделать такой класс декоратор, чтобы именно он принимал на вход функцию?

Поясню. Вот сейчас у меня есть так называемый класс функтор с переопределённым методом __call__ и его экземпляры могут быть декораторами:

from functools import wraps


class FuncDec:
    def __init__(self):
        pass

    def __call__(self, f):
        @wraps(f)
        def wrapper(*args, **kwargs):
            f(*args, **kwargs)
            print('I am working properly')
        return wrapper

func_dec = FuncDec()

@func_dec
def func():
    print('Hello')

func()
# Hello
# I am working properly

Собственно вопрос в том, как сделать, чтобы я мог декорировать функцию не с помощью (или не только) @func_dec, а с помощью @FuncDec. Я не понимаю, как и что надо прописать в __init__, чтобы декоратором стал именно класс.

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

from functools import wraps
class Repeater:
    def __init__(self, n):
        self.n = n
    def __call__(self, f):
        @wraps(f)
        def wrapper(*args, **kwargs):
            for _ in range(self.n):
                f(*args, **kwargs)
        return wrapper
@Repeater(3)
def foo():
    print('foo')
foo() 

Заранее большое спасибо!


Ответы (3 шт):

Автор решения: vp_arth

В Repeater(3) декоратор — тоже экземпляр

Так подойдёт?

from functools import wraps

class Repeater:
    @staticmethod
    def repeat(n):
        def inner(fn):
            @wraps(fn)
            def wrapper(*args, **kwargs):
                for _ in range(n):
                    fn(*args, **kwargs)

            return wrapper
        return inner

@Repeater.repeat(3)
def foo():
    print('foo')

foo()
→ Ссылка
Автор решения: Danis

примерно так:

class FuncDec:
    def __init__(self, func):
        self.func = func

    def __call__(self, *args, **kwargs):
        self.func(*args, **kwargs)
        print('I am working properly')

@FuncDec
def func():
    print('Hello')

@Repeater(3)
def foo():
    print('foo')
foo() 

можно записать так:

repeater = Repeater(3)# создаём экземпляр класс
foo = repeater(foo) # в foo записываем функцию wrapper
foo() # вызываем wrapper

а ваш начальный код так:

funcdec = FuncDec() # создаём экземпляр класс
foo = funcdec(foo) # в foo записываем функцию wrapper
foo() # вызываем wrapper

а мой код можно записать так:

foo = FuncDev(foo) # foo становится экземпляром класса FuncDev
foo() # вызываем __call__ нашего класса

в первых двух вариантах в foo лежит wrapper, а в моем варианте экземпляр класса

→ Ссылка
Автор решения: roddar92

Поскольку у вас пустой конструктор, то и декоратор вызывается с конструктором. По сути дела, у вас func_dec = FuncDec() заменяется обычным вызовом конструктора с собачкой:

@FuncDec()
def func():
    print('Hello')
→ Ссылка