0

I have created a class that I"m attempting to implement a Singleton pattern on. When __new__ for the class is fired off, I check to see if a class-wide list of instances has a length > 0 or not, and only return the class instance if it's 0 (i.e there isn't already an instance). I've gotten this to MOSTLY work, and can validate that it isn't intstaniating additional objects - however, the reference still exists, even though it returns None. I'm not sure if there is a way to avoid this, as ideally I don't want an empty reference if the instance isn't returned.

The code:

class Dog(Mammal):
    _instances = []
    _type: str

    def __new__(cls,type):
        if len(cls._instances) == 0:
            instance = super().__new__(cls)
            print("no classes yet")
            return instance
        else:
            return

    def __init__(self,type):
        self._type = type
        print("creating a new {} dog".format(self._type))
        Dog._instances.append(self)


    def sleep(self):
        return "Zzzzz...."

    @classmethod
    def totalDogs(cls):
        return len(cls._instances)

# Returns a viable Dog object 
fido = Dog("Yorkie")
print(fido.sleep())
rey = Dog("Matlese")
# Returns None, but doesn't throw an error since the reference (rey) still exists:
print(rey)

# Validates that there is only 1 instance
print(fido.totalDogs())

While it's clear an instance hasn't been created and assigned to 'rey', the reference is still valid, although returns None. Is there any way to avoid this? Do I need to raise an Error of some type? Or simply rely on garbage collection?

3
  • 4
    Singleton usually means that you return an already instantiated object without constructing a new one. Not just return nothing. The alternative would be to prevent instantiation by raising an error, yes. But you can’t have the instantiation succeed without error and not return anything and also not have None. There’s just nothing left it could be. Commented Oct 30, 2024 at 22:06
  • In Python, return with no argument is equivalent to return None. So what are you expecting it to assign to rey? Commented Oct 30, 2024 at 22:10
  • If you try to use any Dog methods on rey you'll get an error, since it doesn't actually contain a Dog instance. Commented Oct 30, 2024 at 22:11

1 Answer 1

0

The thing here is that __new__ in the class should return the instance itself, whether there is a cache-hit (for the singleton instance) or not.

Another thing to be aware with this approach is that whenever __new__ returns a valid instance, __init__ will be called, even if it is not a fresh instance. So we need to include an extra state-check to prevent __init__ from running on the subsequent instantiation attempts:

class Dog(Mammal):
    _instances = []
    _type: str
    _initialized: bool

    def __new__(cls,type):
        # In Python, an empty sequence is already falsy in a boolean context.
        # no need to check `len`
        if  not cls._instances:
            instance = super().__new__(cls)
            # your snippet was not saving the instance in the cache
            cls._instances.append(instance)
            print("no classes yet")
            cls._initialized = False
        
        return cls._instances[0]


    def __init__(self,type):
        if self._initialized:
            # skip init
            return
        self._type = type
        print("creating a new {} dog".format(self._type))
        Dog._instances.append(self)
        # signal that `__init__` had already run for the singleton:
        type(self)._initialized = True
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.