Do all decorator classes need __call__?
decorator class should contain __call__ or __new__ method
Not all decorator classes need to implement __call__.
It's only required when we want to call the decorated object with ().
- A decorator class that takes a callable to produce a callable has to implement - __call__.
In this example,- __call__is implemented because we want to do- data.calculate().
 - # Decorator to call and cache the function immediately
class PreCompute:
    def __init__(self, func):
        self.value = func()
    def __call__(self, *args, **kwds):
        return self.value
class Data:
    @PreCompute
    def calculate():
        print("Data.calculate called")
        return 42
data = Data()
# This actually calls PreCompute's __call__
print(data.calculate())
 - The definition of - class Datahere is roughly desugared to something like this,
so when calling- data.calculate()we're actually calling the- __call__function from- class PreCompute.
 - class Data:
    def calculate():
        print("Data.calculate called")
        return 42
    calculate = PreCompute(calculate)
 
- A decorator class that takes a callable but does not produce a callable does not have to implement - __call__.
For example, we can modify the- class Precomputedecorator to the following code, which allows us to access- data.calculateas if it's an attribute.
For more information about what- __get__does, see Descriptor HowTo Guide from Python docs.
 - class PreCompute:
  def __init__(self, func):
      self.value = func()
  def __get__(self, instance, owner):
      return self.value
class Data:
    @PreCompute
    def calculate():
        print("Data.calculate called")
        return 42
data = Data()
# Access .calculate like an attribute
print(data.calculate)
 
What about __new__?
I'm not sure how OP got the impression that decorator classes must define either __call__ or __new__. I've seen __new__ being defined for use cases like @singleton decorator for classes, but as discussed in the previous section about __call__, this is also not strictly required. The only function we must define is an __init__ that receives the object to be decorated.
How does @functools.cached_property work, then?
Now going back to the question, notice from the documentation of @functools.cached_property that
it "transform a method of a class into a property", which is to be accessed without the parentheses ().
Therefore, class cached_property implements __get__ but not __call__, which is similar to the second example above.