关于python:如果已经存在,则返回类实例而不是创建新实例

Return class instance instead of creating a new one if already existing

我为我正在进行的一些实验室实验的结果定义了一个名为Experiment的类。其想法是创建一种数据库:如果我添加一个实验,在退出前将被pickle到一个数据库中,在启动时重新加载(并添加到类注册表中)。

我的班级定义是:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class IterRegistry(type):
    def __iter__(cls):
        return iter(cls._registry)


class Experiment(metaclass=IterRegistry):
    _registry = []
    counter = 0

    def __init__(self, name, pathprotocol, protocol_struct, pathresult, wallA, wallB, wallC):
        hashdat = fn.hashfile(pathresult)
        hashpro = fn.hashfile(pathprotocol)
        chk = fn.checkhash(hashdat)
        if chk:
            raise RuntimeError("The same experiment has already been added")
        self._registry.append(self)
        self.name = name
        [...]

虽然fn.checkhash是一个检查包含结果的文件哈希的函数:

1
2
3
4
5
def checkhash(hashdat):
    for exp in cl.Experiment:
        if exp.hashdat == hashdat:
            return exp
    return False

这样,如果我添加一个以前添加的实验,这将不会被覆盖。

如果已经存在,是否可以以某种方式返回现有实例,而不是引发错误?(我知道在__init__区是不可能的)


如果要自定义创建而不只是在新创建的对象中初始化,则可以使用__new__

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Experiment(metaclass=IterRegistry):
    _registry = []
    counter = 0

    def __new__(cls, name, pathprotocol, protocol_struct, pathresult, wallA, wallB, wallC):
        hashdat = fn.hashfile(pathresult)
        hashpro = fn.hashfile(pathprotocol)
        chk = fn.checkhash(hashdat)
        if chk:                      # already added, just return previous instance
            return chk
        self = object.__new__(cls)   # create a new uninitialized instance
        self._registry.append(self)  # register and initialize it
        self.name = name
        [...]
        return self                  # return the new registered instance


试着这样做(非常简单的例子):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class A:
    registry = {}

    def __init__(self, x):
        self.x = x

    @classmethod
    def create_item(cls, x):
        try:
            return cls.registry[x]
        except KeyError:
            new_item = cls(x)
            cls.registry[x] = new_item
            return new_item


A.create_item(1)
A.create_item(2)
A.create_item(2)  # doesn't add new item, but returns already existing one