首页 > 解决方案 > 仅在不存在时创建新实例,否则返回现有而不进行新初始化

问题描述

我想存储类本身的实例。如果该__new__方法正在调用它应该返回该实例(如果存在),但没有新的初始化。

我写了一个最小的例子:

from pprint import pprint


class A:
    _l = {}

    def __init__(self, name : str, initalize: bool = True, **kwargs) -> None:
        print("In init")
        if kwargs.get('initalize'):
            print("no initialization")
            print(self.numbers)
            self.name = self.name
            self.numbers = self.numbers
            return
        self.name = name
        self.numbers = []

    def __new__(cls, name:str, *args, **kwargs):
        a = A._l.get(name)

        if a:
            print("Use existing A")
            kwargs['initalize'] = False
            return a
        
        print(f"Create new A object with name: {name}")
        cls._l[name] = super(A, cls).__new__(cls)
        return cls._l[name]



l = [
        A('Alex'), A('Gerrit'), A('Jannis'), A('Hannes'),
        A('Hannes'), A('Alex'), A('Mumpitz')
    ]

print("\n#### 1 ####")
c = 0
for a in l:
    a.numbers.append(1)
    a.numbers.append(2)
    print(f"[{c}] {a} : {a.numbers}")
    c += 1

print("\n#### 2 ####")
for n in ['Alex', 'Mumpitz']:
    current = A(n)
    pprint(f"current: {current}")
    current.numbers.append(3)

print("\n#### 3 ####")
for a in l:
    print(a.numbers)

输出如下所示:

Create new A object with name: Alex
In init
Create new A object with name: Gerrit
In init
Create new A object with name: Jannis
In init
Create new A object with name: Hannes
In init
Use existing A
In init
Use existing A
In init
Create new A object with name: Mumpitz
In init

#### 1 ####
[0] <__main__.A object at 0x7f7b2af87d30> : [1, 2]
[1] <__main__.A object at 0x7f7b2af87cd0> : [1, 2]
[2] <__main__.A object at 0x7f7b2af87be0> : [1, 2]
[3] <__main__.A object at 0x7f7b2af874c0> : [1, 2]
[4] <__main__.A object at 0x7f7b2af874c0> : [1, 2, 1, 2]
[5] <__main__.A object at 0x7f7b2af87d30> : [1, 2, 1, 2]
[6] <__main__.A object at 0x7f7b2af87460> : [1, 2]

#### 2 ####
Use existing A
In init
'current: <__main__.A object at 0x7f7b2af87d30>'
Use existing A
In init
'current: <__main__.A object at 0x7f7b2af87460>'

#### 3 ####
[3]
[1, 2]
[1, 2]
[1, 2, 1, 2]
[1, 2, 1, 2]
[3]
[3]

我想要的是?第三个输出应该类似于以下代码,但没有创建外部类或持有者。

#### 3 ####
[1, 2, 3] 
[1, 2]
[1, 2]
[1, 2, 1, 2]
[1, 2, 1, 2]
[1, 2, 3]
[1, 2, 3]

标签: pythonreferenceinitializationpython-3.9python-class

解决方案


首先,我尝试以最少的更改完成您的代码。如果初始化发生与否,我添加了一个测试,而不是不工作initialize标志:hasattr

class A:
    _l = {}

    def __init__(self, name : str, **kwargs) -> None:
        print("In init")
        if hasattr(self, 'name'):
            print("no initialization")
            return
        self.name = name
        self.numbers = []

    def __new__(cls, name:str, *args, **kwargs):
        a = A._l.get(name)
        if a:
            print("Use existing A")
            return a
    
        print(f"Create new A object with name: {name}")
        cls._l[name] = super(A, cls).__new__(cls)
        return cls._l[name]

我正在添加一个我觉得更简单的工厂函数的替代方案:

class _A: 
    def __init__(self, name : str) -> None:
        print(f"In init A({name})")
        self.name = name
        self.numbers = []

def A(name, _instances={}):  # deliberate use of a mutable default arg
    if name not in _instances:
        _instances[name] = _A(name)
    return _instances[name]

首先,我正在考虑另一种选择,其中添加dict了一个__missing__函数的子类,但都A(name)必须替换为A[name],所以我放弃了这个想法。


但是,输出并不完全符合预期:

#### 3 ####
[1, 2, 1, 2, 3]
[1, 2]
[1, 2]
[1, 2, 1, 2]
[1, 2, 1, 2]
[1, 2, 1, 2, 3]
[1, 2, 3]

但我希望它是正确的。


推荐阅读