簡體   English   中英

如果已經存在,則返回類實例而不是創建一個新實例

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

我為我正在進行的一些實驗室實驗的結果定義了一個名為Experiment的類。 這個想法是創建一種數據庫:如果我添加一個實驗,這將在退出之前被腌制到一個數據庫並在啟動時重新加載(並添加到類注冊表)。

我的類定義是:

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是一個函數,用於檢查包含結果的文件的哈希值:

def checkhash(hashdat):
    for exp in cl.Experiment:
        if exp.hashdat == hashdat:
            return exp
    return False

因此,如果我添加之前添加的實驗,則不會被覆蓋。

如果已經存在而不是引發錯誤,是否有可能以某種方式返回現有實例? (我知道在__init__塊中這是不可能的)

嘗試以這種方式進行操作(非常簡化的示例):

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

如果要自定義創建,而不僅僅是在新創建的對象中進行初始化,則可以使用__new__

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

經過四年的提問,我來到這里,Serge Ballesta 的回答幫助了我。 我用更簡單的語法創建了這個例子。

如果baseNone ,它將始終返回創建的第一個對象。

class MyClass:
    instances = []

    def __new__(cls, base=None):
        if len(MyClass.instances) == 0:
            self = object.__new__(cls)
            MyClass.instances.append(self)

        if base is None:
            return MyClass.instances[0]
        else:
            self = object.__new__(cls)
            MyClass.instances.append(self)
            # self.__init__(base)
            return self

    def __init__(self, base=None):
        print("Received base = %s " % str(base))
        print("Number of instances = %d" % len(self.instances))
        self.base = base


R1 = MyClass("apple")
R2 = MyClass()
R3 = MyClass("banana")
R4 = MyClass()
R5 = MyClass("apple")

print(id(R1), R1.base)
print(id(R2), R2.base)
print(id(R3), R3.base)
print(id(R4), R4.base)
print(id(R5), R5.base)
print("R2 == R4 ? %s" % (R2 == R4))
print("R1 == R5 ? %s" % (R1 == R5))

它給了我們結果

Received base = apple 
Number of instances = 2
Received base = None 
Number of instances = 2
Received base = banana 
Number of instances = 3
Received base = None 
Number of instances = 3
Received base = apple 
Number of instances = 4
2167043940208 apple
2167043940256 None
2167043939968 banana
2167043940256 None
2167043939872 apple
R2 == R4 ? True
R1 == R5 ? False

很高興知道__init__將始終在__new__ return之前被調用,即使您沒有調用它(在注釋部分)或者您返回一個已經存在的對象。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM