Como posso memorizar uma instanciação de classe em Python?
Ok, aqui está o cenário do mundo real: estou escrevendo um aplicativo e tenho uma classe que representa um certo tipo de arquivo (no meu caso, são fotografias, mas esse detalhe é irrelevante para o problema). Cada instância da classe Photograph deve ser exclusiva para o nome do arquivo da foto.
O problema é que, quando um usuário informa ao meu aplicativo para carregar um arquivo, eu preciso ser capaz de identificar quando os arquivos já estão carregados e usar a instância existente para esse nome, em vez de criar instâncias duplicadas no mesmo nome de arquivo.
Para mim, isso parece uma boa situação para usar a memoização, e há muitos exemplos disso lá fora, mas neste caso eu não apenas estou memorizando uma função comum, eu preciso estar memoizando__init__()
. Isso representa um problema, porque no momento__init__()
é chamado já é tarde demais já que há uma nova instância criada.
Na minha pesquisa eu encontrei o Python__new__()
método, e eu era realmente capaz de escrever um exemplo trivial de trabalho, mas ele se desfez quando eu tentei usá-lo em meus objetos do mundo real, e eu não sei por que (a única coisa que posso pensar é que o meu real objetos do mundo eram subclasses de outros objetos que eu realmente não posso controlar, então havia algumas incompatibilidades com essa abordagem). Isso é o que eu tive:
class Flub(object):
instances = {}
def __new__(cls, flubid):
try:
self = Flub.instances[flubid]
except KeyError:
self = Flub.instances[flubid] = super(Flub, cls).__new__(cls)
print 'making a new one!'
self.flubid = flubid
print id(self)
return self
@staticmethod
def destroy_all():
for flub in Flub.instances.values():
print 'killing', flub
a = Flub('foo')
b = Flub('foo')
c = Flub('bar')
print a
print b
print c
print a is b, b is c
Flub.destroy_all()
Qual saída isso:
making a new one!
139958663753808
139958663753808
making a new one!
139958663753872
<__main__.Flub object at 0x7f4aaa6fb050>
<__main__.Flub object at 0x7f4aaa6fb050>
<__main__.Flub object at 0x7f4aaa6fb090>
True False
killing <__main__.Flub object at 0x7f4aaa6fb050>
killing <__main__.Flub object at 0x7f4aaa6fb090>
Está perfeito! Apenas duas instâncias foram feitas para os dois IDs únicos, e o Flub.instances claramente tem apenas dois listados.
Mas quando tentei usar essa abordagem com os objetos que estava usando, recebi todos os tipos de erros absurdos sobre como__init__()
levou apenas 0 argumentos, não 2. Então, eu mudaria algumas coisas e então me diria isso__init__()
precisava de um argumento. Totalmente bizarro.
Depois de um tempo lutando com isso, eu basicamente desisti e mudei todo o__new__()
magia negra em um método estático chamadoget
, de tal forma que eu poderia chamarPhotograph.get(filename)
e só chamariaPhotograph(filename)
se o nome do arquivo já não estavaPhotograph.instances
.
Alguém sabe onde eu errei aqui? Existe alguma maneira melhor de fazer isso?
Outra maneira de pensar sobre isso é que é semelhante a um singleton, exceto que não é globalmente singleton, apenas singleton-per-filename.
Aqui está o meu código do mundo real usando o método static get se você quiser ver tudo junto.