0

I am defining a Python singleton as follows:

class Database:
    initialized = False

    def __init__(self):
        self.id = random.randint(1,101)
        print("Generated an id of ", self.id)
        print("Loading database from file")

    _instance = None

    def __new__(cls, *args, **kwargs):
        if not cls._instance:
            cls._instance = super(Database, cls)\
                .__new__(cls, *args, **kwargs)
        return cls._instance

This works in the sense that every single call to Database() actually returns one and only instance. However, the __init__() method is still called on every invocation. For example,

database = Database()

if __name__ == '__main__':
    d1 = Database()
    d2 = Database()

    print(d1.id, d2.id)
    print(d1 == d2)
    print(database == d1)

Produces the output

Generated an id of  8
Loading database from file
Generated an id of  89
Loading database from file
Generated an id of  81
Loading database from file
81 81
True
True

Why is that? What can I do to avoid the initializer being called more than once?

martineau
  • 119,623
  • 25
  • 170
  • 301
Dmitri Nesteruk
  • 23,067
  • 22
  • 97
  • 166
  • 1
    A workaround would be to set `cls.initialized` to `True` in `__new__()` and then check its value in the `__init__()` method (and don't do anything when it's `True`). – martineau Jan 21 '19 at 11:12
  • @martineau this does not work. if I cordon off everything with `if not self.initialized`, trying to access the database's `id` results in an error. – Dmitri Nesteruk Jan 22 '19 at 14:22
  • Just for the sake of completeness -- similar question and underlying problem was addressed here https://stackoverflow.com/questions/674304/why-is-init-always-called-after-new (not exactly a duplicating question though) – VDV Feb 01 '19 at 11:57

1 Answers1

1

Creating an instance is a two steps routine: ① Create a "blank" instance by calling cls.__new__(), then ② initialize this blank instance by calling obj.__init__().

In your case, creating a new instance of your class calls your __new__() which always returns the same (not so blank) instance which then gets initialized properly by calling __init__() in it (sorry for the pun).

I am not sure if there is a way to avoid calling the __init__(), at least no straight-forward one, I think. I would accept this and just keep an empty __init__() which doesn't hurt when called.

Or, maybe it is generally a better idea not to create new instances of your singleton (which then happen to turn out to be the same instance due to your special __new__() method). Instead just use cls.singleton to store your one and only instance (the singleton) or just use the class itself as the singleton and make everything in class and static methods. I typically go for the latter option.

Alfe
  • 56,346
  • 20
  • 107
  • 159