В моем понимании класс декоратора должен содержать метод __call__ или __new__. Но cached_property в репозитории cpython не следует правилам. Может ли кто-нибудь объяснить это для меня?
class cached_property:
def __init__(self, func):
xxx
def __set_name__(self, owner, name):
xxx
def __get__(self, instance, owner=None):
xxx
__class_getitem__ = classmethod(GenericAlias)
Разместите фрагмент кода, а не просто ссылку.
декораторы могут быть вызываемыми объектами, поэтому функции, классы (будут выполнять инициализацию или создание) или экземпляры классов (которые нуждаются в вызове) и должны снова возвращать функцию/вызываемый объект.






Всем ли классам декораторов нужен __call__?
класс декоратора должен содержать метод
__call__или__new__
Не все классы декораторов должны реализовывать __call__.
Это требуется только тогда, когда мы хотим вызвать украшенный объект с помощью ().
Класс декоратора, который принимает вызываемый объект для создания вызываемого объекта, должен реализовать __call__.
В этом примере __call__ реализовано, потому что мы хотим сделать 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())
Определение class Data здесь примерно обессахарено до чего-то вроде этого,
поэтому при вызове data.calculate() мы фактически вызываем функцию __call__ из class PreCompute.
class Data:
def calculate():
print("Data.calculate called")
return 42
calculate = PreCompute(calculate)
Класс декоратора, который принимает вызываемый объект, но не создает вызываемый объект, не должен реализовывать __call__.
Например, мы можем изменить декоратор class Precompute на следующий код, который позволит нам получить доступ к data.calculate, как если бы это был атрибут.
Для получения дополнительной информации о том, что делает __get__, см. Руководство по дескриптору из документации по Python.
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)
А как насчет __new__?
Я не уверен, как у OP сложилось впечатление, что классы декораторов должны определять либо __call__, либо __new__. Я видел, как __new__ определяется для таких случаев использования, как @singleton decorator для классов, но, как обсуждалось в предыдущем разделе о __call__, это также не является строго обязательным. Единственная функция, которую мы должны определить, — это __init__, которая получает декорируемый объект.
Как же тогда работает @functools.cached_property?
Теперь возвращаясь к вопросу, обратите внимание на документации @functools.cached_property что
он «преобразует метод класса в свойство», доступ к которому осуществляется без круглых скобок ().
Поэтому класс cached_property реализует __get__, но не __call__, что аналогично второму примеру выше.
Я опускаю тело функции, чтобы успешно опубликовать сообщение, перейдите по ссылке cpython, чтобы увидеть полный код.