Python单例类

xav*_*ras 1 python firefox singleton selenium-rc

我正在使用Python 2.7在os x 10.6上编写firefox 5.1和selenium webdrive v.2的测试套件.

除了创建单例类之外,一切都工作正常,这应该只保证一个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_()
Run Code Online (Sandbox Code Playgroud)

此时如果我f=Fire() 再次打电话没有任何反应.不会创建新实例.我的问题是为什么我会看到这种行为?我怎么做的?

我的第二个问题,如果我输入:

isinstance(f, Fire)
Run Code Online (Sandbox Code Playgroud)

我收到此错误:

TypeError: isinstance() arg 2 must be a class, type, or tuple of classes and types
Run Code Online (Sandbox Code Playgroud)

这对我来说很奇怪......根据我的理解它应该回归 True

最后一个问题:

当我有单身课时,我应该能够做到:

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
Run Code Online (Sandbox Code Playgroud)

我无法理解这一点.

kin*_*all 6

就创建一个类的单个实例而言,你的装饰器对我来说似乎没问题,所以我没有看到你的问题#1.它没有做你想象的那样:每次你使用装饰器都有一个新的instances字典,而且里面只有一个项目,所以实际上没有任何理由在那里使用字典 - 你需要一个可变容器所以你可以修改它,但我会使用一个列表,或者在Python 3中,也许是一个nonlocal变量.但是,它确实执行其预期的功能,以确保只有一个装饰类的实例.

如果你问为什么在关闭它之后你不能创建一个新的对象实例,那么你没有编写任何代码来允许在那种情况下创建另一个实例,而且Python无法猜测你想要什么那会发生.单例意味着只有一个类的实例.你已经创建了那个实例; 你不能创造另一个.

对于#2,您的@singleton装饰器返回一个函数,该函数实例化(或返回先前创建的实例)该类.因此Fire,一个功能,而不是一个类,一旦装饰,这就是为什么你的isinstance()工作不起作用.

在我看来,对单身人士最直接的方法是将智慧放在一个类而不是装饰器中,然后从该类继承.从继承的角度来看,这甚至是有意义的,因为单例是一种对象.

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
Run Code Online (Sandbox Code Playgroud)

如果你仍然想用装饰器做,最简单的方法是在装饰器中创建一个中间类并返回:

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
Run Code Online (Sandbox Code Playgroud)

您可以将所需的行为注入到现有的类对象中,但在我看来,这是不必要的复杂,因为它需要(在Python 2.x中)创建方法包装器,并且您还必须处理被装饰的类已经有了__new__()自己的方法.

您可能认为可以编写一个__del__()方法,以便在没有对现有实例的引用时创建新的单例.这不起作用,因为总是存在对实例的类内部引用(例如,Fire._instance),因此__del__()永远不会被调用.一旦你有一个单身人士,它就会留下来.如果你在关闭旧单身后想要一个新的单身人士,你可能实际上并不想要一个单身人士而是其他东西.一个上下文管理可能是一种可能性.

在某些情况下可以重新实例化的"单身人士",对我来说,实际上是非常奇怪和意想不到的行为,我会反对它.不过,如果这是你真正想要的,你可以self.__class__._instance = None用你的close_()方法做.或者您可以编写一个单独的方法来执行此操作.它看起来丑陋,这是恰当的,因为它丑陋的.:-)

我认为你的第三个问题也来自这样一个事实,即close_()当你没有编程那种行为时,你希望单身人士在你打电话之后以某种方式消失.