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

时间:2018-11-15 03:35:26

标签: python design-patterns singleton factory-pattern

我想创建一个类,并且所有对象都必须具有唯一的标识符key,并且如果我尝试使用先前存在的键来创建该对象的新实例,则该实例应与已经存在的那个。

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

我的第一个方法是

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不会共享它应该具有的任何属性,并且如果Master类具有任何{ {1}}(下划线一个),它也不会出现。

任何想法我该怎么做?

我认为这与“工厂方法模式”相似,但是我仍然很难找到相似之处或如何以优雅的形式实现。

编辑:

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

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

3 个答案:

答案 0 :(得分:1)

我认为您无法使用The regular expression pattern C:\Program Files\Microsoft SQL Server\MSSQL14.DEV_MIGRATIONS\MSSQL\DATA\Migration_Data.mdf is not valid.方法来执行此操作,因为调用该方法时已经创建了该类的新实例。您可能需要创建一个工厂类型的方法,例如:

__init__()

答案 1 :(得分:1)

您可以使用__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的更改不会影响B,对B的更改不会影响AC。 / p>

编辑:

如果要复制值而不是实例,则可以将值存储在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

答案 2 :(得分:1)

A Kruger答案的启发,我有一个建议的解决方案,它基于__new__方法的使用。该答案的主要区别在于,无需创建内部__Master类。调用__new__时会自动调用Master()方法,并且该方法应返回Master类的实例。在我的回答中,__new__方法在需要时返回一个新实例,但在可能的情况下从existent字典中返回一个实例。请注意,用户照常访问Master类,即他们仅调用Master('A', 0)。通过使Master类扩展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的更改会相互影响,因为它们是同一对象。