Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<p>Your decorator seems to work OK for me as far as creating a single instance of a class, so I don't see your issue #1. It isn't doing quite what you think it is: each time you use the decorator there's a fresh <code>instances</code> dictionary, and there's only ever one item in it, so there's not actually any reason to use a dictionary there -- you need a mutable container so you can modify it, but I'd use a list, or, in Python 3, perhaps a <code>nonlocal</code> variable. However, it does perform its intended function of making sure there's only one instance of the decorated class.</p> <p>If you're asking why you can't create a new instance of the object after closing it, well, you didn't write any code to allow another instance to be created in that situation, and Python is incapable of guessing that you want that to happen. A singleton means there is only ever a single instance of the class. You have created that instance; you <em>can't</em> create another.</p> <p>As for #2, your <code>@singleton</code> decorator returns a function, which instantiates (or returns a previously-created instance) of the class. Therefore <code>Fire</code> is a function, not a class, once decorated, which is why your <code>isinstance()</code> doesn't work.</p> <p>The most straightforward approach to singletons, in my opinion, is to put the smarts in a class rather than in a decorator, then inherit from that class. This even makes sense from an inheritance point of view, since a singleton is a kind of object.</p> <pre><code>class Singleton(object): _instance = None def __new__(cls, *args, **kwargs): if not cls._instance: cls._instance = object.__new__(cls, *args, **kwargs) return cls._instance class Fire(Singleton): pass f1 = Fire() f2 = Fire() f1 is f2 # True isinstance(f1, Fire) # True </code></pre> <p>If you still want to do it with a decorator, the simplest approach there would be to create an intermediate class in the decorator and return that:</p> <pre><code>def singleton(D): class C(D): _instance = None def __new__(cls, *args, **kwargs): if not cls._instance: cls._instance = D.__new__(cls, *args, **kwargs) return cls._instance C.__name__ = D.__name__ return C @singleton class Fire(object): pass </code></pre> <p>You could inject the desired behavior into the existing class object, but this is, in my opinion, needlessly complex, as it requires (in Python 2.x) creating a method wrapper, and you also have to deal with the situation in which the class being decorated already has a <code>__new__()</code> method yourself.</p> <p>You might think that you could write a <code>__del__()</code> method to allow a new singleton to be created when there are no references to the existing instance. This won't work because there is always a class-internal reference to the instance (e.g., <code>Fire._instance</code>) so <code>__del__()</code> is never called. Once you have a singleton, it's there to stay. If you want a new singleton after you close the old one, <em>you probably don't actually want a singleton</em> but rather something else. A <a href="http://www.python.org/dev/peps/pep-0343/" rel="nofollow">context manager</a> might be a possibility.</p> <p>A "singleton" that can be re-instantiated under certain circumstances would be, to me, really weird and unexpected behavior, and I would advise against it. Still, if that's what you really want, you could do <code>self.__class__._instance = None</code> in your <code>close_()</code> method. Or you could write a separate method to do this. It looks ugly, which is fitting because it <em>is</em> ugly. :-)</p> <p>I think your third question also arises from the fact that you expect the singleton to somehow go away after you call <code>close_()</code> on it, when you have not programmed that behavior.</p>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload