为同一对象的另一个实例更改“self”?

Changing `self` for another instance of same object?

我想创建一个 class,并且所有对象都需要有一个唯一标识符 key,并且如果我尝试使用以前存在的键创建对象的新实例,则实例应该与已经存在的实例相同。

类似于单例class,但在这种情况下而不是一个class,有很多但不同。

我的第一个方法是这样

class Master:
    existent = {}
    def __init__(self, key, value=None):
        try:
            self = Master.existent[key]
            return 
        except KeyError:
            Master.existent[key] = self
        # Rest of the __init__ method

但是当我比较两个对象时,比如 A = Master('A', 0)B = Master('B', 0)B 没有共享它应该有的任何属性,如果主 class有任何_method(单下划线),也没有出现

知道我该怎么做吗?

我认为这类似于工厂方法模式,但我仍然无法找到相似之处,或者如何以优雅的形式实现。

编辑:

class 基本上有两个属性,仅此而已,但很多东西会继承 and/or 包含此类型的实例,easy 方式我我以为我能做到,是从现有实例中提取对应于所述 key 的属性,将它们分配给新实例并滥用它们将具有相同的 hash 输出和 equal 运算符将根据哈希值运行,因此我可以毫无问题地使用 ==is 运算符。

这个想法解决了我的问题,但总的来说,我认为这可能是一个常见或有趣的场景,需要解决。

我认为您不能使用 __init__() 方法来做到这一点,因为在调用该方法时已经创建了 class 的新实例。您可能需要创建一个类似于以下内容的工厂类型方法:

class Master:
    existent = {}
    init_OK = False

    def __init__(self, key, value=None):
        if not Master.init_OK:
            raise Exception('Direct call to Master() is not allowed')
        Master.init_OK = False
        self.key = key
        self.value = value
        Master.existent[key] = self

    @staticmethod
    def make(key, value=None):
        try:
            inst = Master.existent[key]
        except:
            Master.init_OK = True
            inst = Master(key, value=value)
        return inst

可以使用__new__方法来处理。你不想调用 __init__ 除非你想用一个新的键创建一个新对象,并且 __new__ 可以用来在调用 __init__ 之前先检查键是否唯一。

class Master(object):

    instances = {}
    def __new__(cls, key, value=None):
        if key in Master.instances:
            return Master.instances[key]
        else:
            instance = super(Master, cls).__new__(cls)
            Master.instances[key] = instance
            return instance

    def __init__(self, key, value=None):
        self.value = value

然后你可以用

创建对象
>>> A = Master('A',0)
>>> B = Master('B',0)
>>> C = Master('A',1)

由于 AC 具有相同的键,它们将指向相同的对象并且实际上是相同的实例。由于 CA 具有相同的键,因此它更新了它的值。

>>> print(A.value)
1

A 的任何新更改都将在 C 中显示,反之亦然。

>>> A.value = 5
>>> print(C.value)
5

AC 的更改不会影响 BB 的更改不会影响 AC .

编辑:

如果你想复制值而不是实例,你可以只将值存储在 Master.instances 字典中并检查是否已经有键的值。

class Master(object):

    instances = {}
    def __init__(self, key, value=None):
        if key in Master.instances:
            self.value = Master.instances[key]
        else:
            self.value = value
            Master.instances[key] = value

>>> A = Master('A',0)
>>> C = Master('A',1)
>>> print(C.value)
0

A Kruger 回答的启发,我有另一种解决方案,该解决方案是根据建议使用 __new__ 方法构建的。这个答案的主要区别在于不需要创建内部 __Master class。 __new__ 方法在 Master() 被调用时自动调用,并且预期 return Master class 的一个实例。在我的回答中,如果需要,__new__ 方法 return 是一个新实例,但如果可能,return 是 existent 字典中的一个实例。请注意,用户像往常一样访问 Master class,即他们只是调用 Master('A', 0)。这可以通过使 Master class 扩展 object.

来实现

代码如下:

class Master(object):
    existent = {}

    def __init__(self, key, value=None):
        self.key = key
        self.value = value
        if not key in Master.existent:
            Master.existent[key] = self

    def __new__(cls, *args, **kwargs):
        key = args[0]
        if key in Master.existent:
            return Master.existent[key]
        else:
            return super(Master, cls).__new__(cls)

    def __str__(self):
        return('id: ' + str(id(self)) + ', key=' + str(self.key) + ', value=' + str(self.value))

A = Master('A', 0)
print('A = ' + str(A))
B = Master('A', 1)
print('\nAfter B created:')
print('B = ' + str(B))
print('A = ' + str(A))
B.value = 99
print('\nAfter B modified:')
print('B = ' + str(B))
print('A = ' + str(A))
C = Master('C', 3)
print('\nC = ' + str(C))

这是输出:

A = id: 140023450750200, key=A, value=0

After B created:
B = id: 140023450750200, key=A, value=1
A = id: 140023450750200, key=A, value=1

After B modified:
B = id: 140023450750200, key=A, value=99
A = id: 140023450750200, key=A, value=99

C = id: 140023450750256, key=C, value=3

注意AB有相同的id(它们是同一个对象)。另请注意,对 AB 的更改会相互影响,因为它们是同一对象。