好的,这里是真实场景:我正在编写一个应用程序,并且我有一个表示某种类型文件的类(在我的情况下,这是照片,但该细节与问题无关)。每个Photograph类的实例都应该对应于照片的文件名。
问题是,当用户告诉我的应用程序加载文件时,我需要能够识别已经加载的文件,并使用该文件名的现有实例,而不是在相同的文件名上创建重复实例。
对我来说,这似乎是使用记忆化的好情况,而且有很多相关的示例,但在这种情况下,我不仅需要记忆普通函数,还需要记忆__init__()
。这会带来一个问题,因为在调用__init__()
之前,它已经太晚了,因为已经创建了一个新实例。
在我的研究中,我发现了Python的__new__()
方法,我实际上能够编写一个可工作的简单示例,但当我尝试将其用于我的真实对象时,它就不起作用了,我不确定为什么(唯一能想到的是我的真实对象是其他我无法控制的对象的子类,因此存在一些兼容性问题)。这是我拥有的:
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()
这段代码的输出结果是什么:
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>
太完美了!只有两个唯一ID的实例被创建了,而Flub.instances明显只列出了两个。
但是,当我尝试将这种方法用于我的对象时,我遇到了各种荒谬的错误,例如__init__()只需要0个参数,而不是2个。所以我改变了一些东西,然后它告诉我__init__()需要一个参数。非常奇怪。
经过一段时间的抗争,我基本上放弃了,并将所有__new__()黑魔法移动到一个名为get的staticmethod中,这样我就可以调用Photograph.get(filename),如果filename已经存在于Photograph.instances中,它就只会调用Photograph(filename)。
有人知道我在哪里错了吗?有没有更好的方法来做到这一点?
另一种思考方式是,它类似于一个单例模式,但它不是全局单例模式,而是每个文件名的单例模式。