O Frozenset é adequado para o armazenamento em cache de dados de entrada simétrica em um ditado de Python?
Pergunta
O título mais ou menos diz tudo:
Eu tenho uma função que exige entrada simétrica em dois argumentos, por exemplo, algo como
def f(a1, a2):
return heavy_stuff(abs(a1 - a2))
Agora, quero apresentar algum método de cache. Seria correto / pitônico / razoavelmente eficiente fazer algo assim:
cache = {}
def g(a1, a2):
fs =frozenset((tuple(a1), tuple(a2)))
if fs not in cache:
cache[fs] = f(a1, a2)
return cache[fs]
Ou haveria uma maneira melhor?
Editar: A1 e A2 podem ser as fileiras de uma matriz numpy; É por isso que eu os envolvo em uma tupla cada.
Solução
O Python sempre calcula todos os argumentos que você está passando para uma função e só então chama a função. Em outras palavras, como a maioria dos outros idiomas, o Python é "ansioso" em sua avaliação (a principal exceção hoje é provavelmente Haskell, mas isso não o ajuda ;-).
Então setdefault
é um muito Abordagem inadequada para armazenamento em cache! Sempre que você faz
cache.setdefault(akey, f(x, y))
você é primeiro chamando f(x, y)
, com todo o seu custo computacional, então talvez retirando os resultados desse cálculo no chão; Isso torna o cache totalmente ineficaz.
Em vez disso, sempre faça o seguinte:
akey = whatever(x, y)
if akey not in cache:
cache[akey] = f(x, y)
return cache[akey]
ou similares - existem alguns outros idiomas possíveis, especialmente se você sabe que f
nunca voltará None
:
result = cache.get(akey)
if result is None:
result = cache[akey] = f(x, y)
return result
Quanto à questão secundária do que é apropriado whatever
para o cálculo principal, dado que você sabe que f
é simétrico, eu acho frozenset
provavelmente está ok; embora (se os componentes de x
e y
são comparáveis, bem como hashable - ou seja, não funcionaria com números complexos), você pode querer considerar
ta1 = tuple(a1)
ta2 = tuple(a2)
if ta1 > ta2: key = ta1, ta2
else: key = ta2, ta1
O desempenho relativo depende do custo de comparação, vs do hash, os itens em a1
e a2
. É provável que as diferenças sejam pequenas, de qualquer maneira.