I want to create a class, and all objects need to have a unique identifier key
, and If I attempt to create a new instance of the object with a previously existent key, the instance should be the same as the one that already existing.
Similar to a singleton class, but in this case instead of one class, there are many but different.
My first approach was this
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
But when I compare two objects, something like this A = Master('A', 0)
and B = Master('B', 0)
, the B
doesn't share any attributes that It should have, and if the Master class has any _method
(single underscore), It also doesn't appear.
Any Idea how could I do this?
I think this is similar to the Factory Methods Pattern, but I'm still having trouble to find the parallels, or how to implemented in an elegant form.
EDIT:
The class basically has two proprieties and that's it, but many things would Inherit and/or contain instances of this as type, the easy way I thought I could do it, was extracting the properties from the existing instance corresponding to said key
, assigning them to the new instance and abuse from the fact that they will have same hash
output and the the equal
operator will behave according to hashes so I can use ==
and is
operators with no problem.
This Idea solves my problem, but overall I think this could be a common or interesting enough scenario to tackle.
Inspired by the answer from A Kruger
, I have another solution building off the use of the __new__
method as suggested. The main difference in this answer is that there is no need to create an inner __Master
class. The __new__
method is automatically called when Master()
is invoked, and is expected to return an instance of the Master
class. In my answer, the __new__
method returns a new instance, if needed, but returns an instance from the existent
dictionary, if possible. Note that the user accesses the Master
class as usual, i.e., they just call Master('A', 0)
. This is made possible by making the Master
class extend object
.
Here is the code:
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))
And here is the output:
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
Note that A
and B
have the same id
(they are the same object). Also note that changes to A
or B
affect each other, since they are the same object.