Знание, если кешированное свойство python было получено без фактического доступа к нему - PullRequest
0 голосов
/ 14 января 2020

Существует множество примеров декораторов методов, которые преобразуют метод в кэшированное свойство. Однако иногда я хотел бы проверить, является ли кэш «активным», что означает, что к атрибуту обращались и кэш был заполнен.

Например, если я использую кэшированный rows для хранения таблицы sql в rows, я бы хотел вычислить длину моей таблицы на основе кэша, если он заполнен, но через отдельный sql вызов, если нет. Как мне проверить, был ли получен доступ к rows, не вызывая его доступа?

Вот хороший декоратор, взятый из "Python Cookbook" Дэвида Бизли), который я использую для своих потребностей в кэшированных свойствах. Я улучшил его, чтобы включить мой текущий взлом.

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

    def __get__(self, instance, cls):
        if instance is None:
            return self
        else:
            value = self.func(instance)
            setattr(instance, self.func.__name__, value)
            setattr(instance, self.func.__name__ + '__cache_active', True)  # my hack
            return value

Пример использования:

>>> class Test:
...     def __init__(self, a):
...         self.a = a
...     @lazyprop
...     def len(self):
...         print('generating "len"')
...         return len(self.a)
>>> t = Test([0, 1, 2])
>>> # See what happens if I ask if there is a 'len' attribute:
>>> hasattr(t, 'len')
generating "len"
3
>>> t.len
5

Так что hasattr фактически вызывает вызов метода len, поэтому я могу Не используйте это. В любом случае, я не хотел бы использовать его, потому что я не спрашиваю о существовании атрибута (ключ / ссылка), но о существовании (то есть до вычисления) его значения.

Учитывая строку, помеченную «моим хаком», теперь я могу сделать следующее:

def has_active_cache(instance, attr):
    return getattr(instance, attr + '__cache_active', False)
>>> t = Test([0, 1, 2])
>>> print("Accessed:", has_active_cache(t, 'len'))
Accessed: False
>>> t.len
generating "len"
3
>>> print("Accessed:", has_active_cache(t, 'len'))
Accessed: True

Но я считаю, что есть более изящное решение, чем это. Возможно, тот, который будет «связан» с самим lazyprop ...

1 Ответ

1 голос
/ 14 января 2020

Как и в любом случае, кэширование свойств является частью стандартной библиотеки Python 3.8 через functools

https://docs.python.org/3/library/functools.html?highlight=s#functools .cached_property

с использованием этого декоратор, вы можете получить доступ к атрибуту __dict__ вашего класса напрямую, чтобы проверить, кэшируется ли значение.

Используя пример из документации ...

import statistics
from functools import cached_property


class DataSet:
    def __init__(self, sequence_of_numbers):
        self._data = sequence_of_numbers

    @cached_property
    def stdev(self):
        return statistics.stdev(self._data)

    @cached_property
    def variance(self):
        return statistics.variance(self._data)

А затем проверить это из ...

ds = DataSet(range(1, 20))
ds.stdev
5.627314338711377
ds.__dict__
{'_data': range(1, 20), 'stdev': 5.627314338711377}
ds.variance
31.666666666666668
ds.__dict__
{'_data': range(1, 20), 'stdev': 5.627314338711377, 'variance': 31.666666666666668}
...