Existe uma maneira canônica de armazenar em cache métodos de instância em python?

9

Eu tenho algumas funções computacionalmente intensivas no meu script python que gostaria de armazenar em cache. Procurei soluções no estouro de pilha e encontrei muitos links:

  1. /programming/4431703/python-resettable-instance-method-memoization-decorator
  2. https://wiki.python.org/moin/PythonDecoratorLibrary#Memoize
  3. http://pythonhosted.org/cachetools/
  4. https://pythonhosted.org/Flask-Cache/ (usei este para aplicativos de balão, mas este não é um aplicativo de balão).

No final, acabei colando isso no meu programa. Parece bastante simples - e funciona bem.

class memoized(object):
    '''Decorator. Caches a function's return value each time it is called.
    If called later with the same arguments, the cached value is returned
    (not reevaluated).
    '''
    def __init__(self, func):
        self.func = func
        self.cache = {}

    def __call__(self, *args):
        if not isinstance(args, collections.Hashable):
            return self.func(*args)
        if args in self.cache:
            return self.cache[args]
        else:
            value = self.func(*args)
            self.cache[args] = value
            return value

    def __repr__(self):
        '''Return the function's docstring.'''
        return self.func.__doc__

    def __get__(self, obj, objtype):
        '''Support instance methods.'''
        return functools.partial(self.__call__, obj)

No entanto, estou me perguntando se existe uma prática recomendada canônica em Python. Acho que assumi que haveria um pacote muito usado para lidar com isso e estou confuso sobre o porquê disso não existir. http://pythonhosted.org/cachetools/ é apenas na versão .6 e a sintaxe é mais complexa do que simplesmente adicionar um decorador @memoize, como em outras soluções.

bernie2436
fonte

Respostas:

12

Não existe uma maneira canônica e exclusivamente pitônica de fazer isso. Nada disso eu sei, de qualquer forma - e estou falando como alguém que olhou e que é o autor de um pacote de memorização bem-sucedido .

No entanto, acredito que sua falta de arte anterior encontrada pode ser um problema de terminologia, tanto quanto qualquer outra coisa. Você pediu cache . Esse é um termo adequado, mas é excessivamente amplo. O armazenamento em cache dos resultados de uma chamada ou função de função específica para uso posterior é mais especificamente chamado de memorização ou memorização . E, de fato, existem muitos pacotes de memorização disponíveis na comunidade , além de muitas receitas ( por exemplo, esta ). Também vi funções de memorização em muitos pacotes de utilitários multiuso. Muitos deles são maduros, fortalecidos pela batalha e constantemente usados ​​na produção - e não apenas no "código da versão 0.6".

Por que o memorando não é mais tratado canônica ou idioma, não posso dizer. Talvez porque haja várias maneiras de realizá-lo com diferentes virtudes e trocas. Ou talvez porque já existam tantas abordagens diferentes em uso. Costumo encontrar recursos - "achatar uma lista de lista" é outro - em que outras comunidades de idiomas convergem avidamente, mas que a comunidade ou os poderes do Python parecem preferir manipular as receitas do que se comprometer com uma API específica. De qualquer forma, se seu código funcionar, seja bem-vindo às fileiras de memorizadores de sucesso!

Atualizar

Como a biblioteca padrão do Python 3 (para 3.2 e posterior) inclui um lru_cachedecorador ( documentação aqui ), devo dizer que parece uma tentativa recente de padronizar o caso de uso de memoização mais comum. O fato de ter chegado tão tarde na evolução do Python é provavelmente o motivo pelo qual não existe uma solução comum, mas para o novo código, é tão próximo do canônico quanto você encontrará.

Jonathan Eunice
fonte
1
O Python 3.8 adicionou o @cached_propertydecorador , que é mais adequado para métodos cujos valores de retorno devem permanecer os mesmos ao longo da vida da instância.
Patrick Brinich-Langlois 07/11/19
0

Como um método de instância pode usar atributos próprios e, em particular, modificá-los, não é possível garantir a correção de um método de instância memorizado arbitrário. Além disso, sua implementação adiciona uma restrição implícita no seu objeto para ser lavável e o cache atingido, dependendo desse hash, você está limitado às classes nas quais pode usá-lo e nos campos que pode adicionar às classes com um método memorizado (ou herdado).

Por esses motivos, se você precisar memorizar uma função, é melhor projetar transformar o método de instância caro em um método estático, passando explicitamente para ele os atributos de objeto necessários como argumentos. Isso libera suas mãos do design da classe e pode melhorar o acerto do cache. Isso também ajuda a simplificar e generalizar o código de memorização. Existem implementações refinadas para este projeto, às vezes permitindo personalizar o tamanho do cache, métodos de duração / invalidação, garantindo segurança do encadeamento, persistência ...

Arthur Havlicek
fonte