好的,这是真实的场景:我正在编写一个应用程序,我有一个类,它表示某种类型的文件(在我的例子中,这是照片,但细节与问题无关)。照片类的每个实例对于照片的文件名都应该是唯一的。
问题是,当用户告诉我的应用程序加载文件时,我需要能够识别文件何时已加载,并使用该文件名的现有实例,而不是在同一文件名上创建重复实例。
对我来说,这似乎是一个很好的使用记忆化的环境,有很多这样的例子,但是在这种情况下,我不仅仅是在记忆一个普通的函数,我需要记忆__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)
。
有人知道我哪里做错了吗?有没有更好的方法来做这个?
另一种思考方式是它类似于单例,只是它不是全局单例,只是每个文件名都是单例。
如果你想一起看的话。
关于你的问题,让我们看两点。
使用备忘录
您可以使用memoization,但是应该修饰类,而不是
__init__
方法。假设我们有一个回忆录:现在你只需要装饰一下教室:
让我们看看测试?
输出如下。请注意,相同的参数产生的返回对象的id相同:
无论如何,我更愿意创建一个函数来生成对象并将其记住。对我来说似乎更干净,但可能是一些无关紧要的小毛病:
使用
__new__
:当然,也可以重写} 这可能会有帮助。基本上,它说总是将
__new__
。几天前我发布了an answer about the ins, outs and best practices of overriding ^{*args, **kwargs
传递给__new__
方法。例如,我更愿意记住一个创建对象的函数,或者甚至编写一个特定的函数,该函数负责从不将对象重新创建为同一个参数。当然,不过,这主要是我的意见,而不是规则。
我最终使用的解决方案是:
然后用这个来装饰类,而不是
__init__
。尽管brandizzi向我提供了关键信息,但他的示例装饰器并没有按预期工作。我发现这个概念非常微妙,但基本上当您在Python中使用decorator s时,您需要理解,被修饰的东西(无论是方法还是类)实际上是由decorator本身替换的。例如,当我试图访问
Photograph.instances
或Camera.generate_id()
(staticmethod)时,我实际上无法访问它们,因为Photograph
实际上并不引用原始Photograph类,而是引用memoized
函数(来自brandizzi的示例)。为了解决这个问题,我必须创建一个decorator类,它实际上从decorated类中获取所有属性和静态方法,并将它们作为自己的属性公开。几乎像一个子类,只是decorator类不知道它将要装饰的类,所以它必须在事实发生后复制属性。
最终的结果是,
memoize
类的任何实例都会成为它所修饰的实际类的几乎透明的包装器,除非尝试实例化它(但实际上调用它)将在它们可用时为您提供缓存副本。到
__new__
的参数也传递到__init__
,因此:您需要接受那里的
flubid
参数,即使您不在__init__
中使用它以下是typeobject.c in Python2.7.3中的相关注释
相关问题 更多 >
编程相关推荐