简体   繁体   中英

Python singleton class

I'm writing a test suite for firefox 5.1 and selenium webdrive v.2 on os x 10.6 with Python 2.7.

Everything is working fine except the creation of a singleton class, which should guarantee only one instance of firefox:

def singleton(cls):
    instances = {}
    def getinstance():
        if cls not in instances:
            instances[cls] = cls()
        return instances[cls]
    return getinstance

@singleton
class Fire(object):
    def __init__(self):
        self.driver = webdriver.Firefox()
    def getdriver(self):
        return self.driver
    def close_(self):
        self.driver.close()
    def get(self, url):
        self.driver.get(url)
        return self.driver.page_source

f  = Fire()
f.close_()

at this point if I call f=Fire() again nothing happens. No new instance will be created. My question is why do I see that behavior? How I do that right?

My second question, if I type:

isinstance(f, Fire)

I get this error:

TypeError: isinstance() arg 2 must be a class, type, or tuple of classes and types

This is strange to me ... from my understanding it should return True

A final question:

when I have a singleton class I should be able to do:

f = Fire()
f2 = Fire()
f2.get('http://www.google.com')
up to here works, but if I say
f.close_()//then
URLError: urlopen error [Errno 61] Connection refused

I can't understand this.

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 instances 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 nonlocal variable. However, it does perform its intended function of making sure there's only one instance of the decorated class.

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 can't create another.

As for #2, your @singleton decorator returns a function, which instantiates (or returns a previously-created instance) of the class. Therefore Fire is a function, not a class, once decorated, which is why your isinstance() doesn't work.

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.

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

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:

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

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 __new__() method yourself.

You might think that you could write a __del__() 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 (eg, Fire._instance ) so __del__() 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, you probably don't actually want a singleton but rather something else. A context manager might be a possibility.

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 self.__class__._instance = None in your close_() method. Or you could write a separate method to do this. It looks ugly, which is fitting because it is ugly. :-)

I think your third question also arises from the fact that you expect the singleton to somehow go away after you call close_() on it, when you have not programmed that behavior.

The issue is your use of that singleton class as a decorator. It isn't a decorator at all, so using it like one doesn't make sense.

A decorator needs to actually return the decorated object - usually a function, but in your case, the class. You're just returning a function. So obviously, when you try and use it in isinstance , Fire no longer refers to a class.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM