我有一个内存密集型类,例如表示高分辨率资源(即:媒体、模型、数据等)的类型,可以使用相同的参数多次实例化,例如多次加载资源的相同文件名。
我想在对象创建时实现某种无界
caching
,以内存重用相同的实例(如果它们具有相同的构造函数参数值)。我不关心一个实例的可变性影响其他共享实例。实现这一目标的最简单的Python方式是什么?
请注意,
singletons
、object-pools
、factory methods
或字段properties
都不符合我的用例。
您可以将工厂函数与 functools.cache:
一起使用import functools
@functools.cache
def make_myclass(*args, **kwargs):
return MyClass(*args, **kwargs)
编辑:显然你可以直接装饰你的类以获得相同的效果:
@functools.cache
class Foo:
def __init__(self, a):
print("Creating new instance")
self.a = a
>>> Foo(1)
Creating new instance
<__main__.Foo object at 0x0000021D7D61FFA0>
>>> Foo(1)
<__main__.Foo object at 0x0000021D7D61FFA0>
>>> Foo(2)
Creating new instance
<__main__.Foo object at 0x0000021D7D61F250>
注意两次
Foo(1)
都调用相同的内存地址。
编辑 2:经过一番尝试后,如果您覆盖
__new__
并在那里进行所有缓存和实例化,您可以获得默认的实例缓存行为:
class Foo:
_cached = {}
def __new__(cls, a, b=3):
attrs = a, b
if attrs in cls._cached:
return cls._cached[attrs]
print(f"Creating new instance Foo({a}, {b})")
new_foo = super().__new__(cls)
new_foo.a = a
new_foo.b = b
cls._cached[attrs] = new_foo
return new_foo
a = Foo(1)
b = Foo(1, 3)
c = Foo(b=3, a=1)
d = Foo(4)
print(a is b)
print(b is c)
print(c is d)
输出:
Creating new instance Foo(1, 3)
Creating new instance Foo(4, 3)
True
True
False
__init__
仍将在__new__
之后被调用,因此您需要在缓存检查后在__new__
中进行昂贵的初始化(或全部)。
上面答案中的装饰器将破坏继承,
isinstance
,issubclass
,并且不能与dataclasses
一起工作。以下是一个更好的替代方案,没有这些缺点:
import functools
from dataclasses import is_dataclass
def cached_class(cls):
@functools.wraps(cls.__new__)
def __new__(cls, *args, **kwargs):
return cls.__cache__(cls, args, tuple(kwargs.items()))
@functools.cache
def __cache__(cls, args, kwargs):
if is_dataclass(cls):
it = object.__new__(cls)
else:
it = cls.__orig_new__(cls, *args, **dict(kwargs))
return it
cls.__cache__ = __cache__
cls.__orig_new__ = cls.__new__
cls.__new__ = __new__
return cls
用作:
@cached_class
class Foo:
...
>>> x = Foo()
>>> y = Foo()
>>> x is y
True