缓存Python类实例

问题描述 投票:0回答:2

我有一个内存密集型类,例如表示高分辨率资源(即:媒体、模型、数据等)的类型,可以使用相同的参数多次实例化,例如多次加载资源的相同文件名。

我想在对象创建时实现某种无界

caching
,以内存重用相同的实例(如果它们具有相同的构造函数参数值)。我不关心一个实例的可变性影响其他共享实例。实现这一目标的最简单的Python方式是什么?

请注意,

singletons
object-pools
factory methods
或字段
properties
都不符合我的用例。

python caching singleton multiple-instances object-pooling
2个回答
4
投票

您可以将工厂函数与 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__
中进行昂贵的初始化(或全部)。


0
投票

上面答案中的装饰器将破坏继承,

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

© www.soinside.com 2019 - 2024. All rights reserved.