No, not even close. If it were the same thing, when you executed obj2.getConteudo()
the message to be displayed should be "hello world", not the default, because you changed the value of this attribute through obj
when he did obj.write("ola mundo")
. If they were Singleton, the objects should, necessarily, share exactly the same state.
The two instances did not share the state for the reason that instance
is a guy immutable (keep this word, it will be important later). Even if it is defined as a class attribute, it will be distinguished between all instances of the same class when it is modified. Thus, when creating the second instance, self.instance
will be null again, regardless of whether previous instances have been created or not.
The simplest way around this problem would be to use a type as a class attribute mutable, such as a list or dictionary. For example:
class TestSingleton:
state = {
'instance': None
}
msg = "variável interna com dados padrão"
def getInstance(self):
if self.state['instance'] is None:
self.state['instance'] = TestSingleton()
return self.state['instance']
def getConteudo(self):
print(self.msg)
def write(self, mensagem):
self.msg = mensagem
Thus, when executing the code:
obj = TestSingleton().getInstance()
obj.write("ola mundo")
obj.getConteudo() # ola mundo
obj2 = TestSingleton().getInstance()
obj2.getConteudo() # ola mundo
obj2.write("Que coisa !")
obj2.getConteudo() # Que coisa !
obj.getConteudo() # Que coisa !
That is, the objects will share the state because they will be exactly the same instance.
Still, this solution would be bad, because in this way there would be three distinct instances, and two will be used only to return the third. Imagine that you control flights at an airport are boarding people on an airplane; it will only be a flight, but you require them to put two other aircraft to serve as a "corridor" for passengers to be able to access the aircraft that will be used in the journey. Can you see the cost of it all?
To demonstrate this, it would be sufficient to separate the instance and execution operations from the method getInstance
, making:
obj1 = TestSingleton() # Aqui, obj1 seria a instância A
obj1 = obj1.getInstance() # E aqui, obj1 receberia a instância C
obj2 = TestSingleton() # Aqui, obj2 seria a instância B
obj2 = obj1.getInstance() # E aqui, obj2 receberia a instância C
At this point, starting the path to the ideal solution, it is necessary to answer a question: i really need only one object, class instance, or I can have multiple objects sharing the state?
In case you can share the state with multiple distinct objects, just use a class attribute with changeable type, similar as was done above using the dictionary. For example:
class Foo:
shared = {
'message': 'Olá mundo'
}
def getMessage(self):
return self.shared['message']
def setMessage(self, message):
self.shared['message'] = message
So something like:
a = Foo()
print(a.getMessage()) # Olá mundo
a.setMessage('Nova mensagem')
b = Foo()
print(b.getMessage()) # Nova mensagem
print('São o mesmo objeto:', b is a) # São o mesmo objeto: False
The two objects will share the state defined in shared
, so much so that when the message is changed in a
, the change is reflected in b
.
Already, if you need them to always be the same object (not just share the state), you can create a base class by defining the constructor method __new__
:
class Singleton:
__instance = None
def __new__(cls, *args, **kwargs):
if Singleton.__instance is None:
Singleton.__instance = super().__new__(cls)
return Singleton.__instance
And so you utilize inheritance in your class:
class Foo(Singleton):
def __init__(self, name):
self.name = name
For example:
a = Foo("Anderson")
print(a.name) # Anderson
b = Foo("Woss")
print(b.name) # Woss
print(a.name) # Woss
print('São o mesmo objeto:', b is a) # São o mesmo objeto: True
Note that when defining b
with a different value, the attribute name
of a
is also changed as they will be the same object.
"unnecessary so much programming technique for a really simple thing" - a Singleton must ensure that one (and only one) instance of the class is created. In multi-threaded environments this is not as simple as it seems, and all this technique is needed to ensure that there is no more than one instance being created. This article explains why it’s not that simple. It uses examples in Java, but reported problems can happen in any language.
– hkotsubo
That’s an interesting article. Note that the Python specification ensures determinism in some respects - in particular, the code that assigns a reference to the instance to a variable will only be executed after all the instance initialization is complete. That is, in Python does not happen the biggest ghost foreseen in this article, which would be the reference to receive a memory address that would not yet be initialized.
– jsbueno
I expanded the response to address the concerns of this article.
– jsbueno