我正在使用 Python 在 os x 10.6 上为 firefox 5.1 和 selenium webdrive v.2 编写测试套件 2.7.
除了创建单例类外,一切正常,这应该保证 只有一个 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_()
此时,如果我再次调用 f=Fire(),什么也不会发生。不会创建新实例。 我的问题是为什么我会看到这种行为? 我该怎么做?
我的第二个问题,如果我输入:
isinstance(f, Fire)
我收到这个错误:
TypeError: isinstance() arg 2 must be a class, type, or tuple of classes and types
这对我来说很奇怪……根据我的理解,它应该返回 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
我无法理解这一点。
请您参考如下方法:
就创建一个类的单个实例而言,你的装饰器对我来说似乎工作正常,所以我没有看到你的问题#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
如果你仍然想用装饰器来做,最简单的方法是在装饰器中创建一个中间类并返回它:
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
您可以将所需的行为注入(inject)现有的类对象,但在我看来,这是不必要的复杂,因为它需要(在 Python 2.x 中)创建一个方法包装器,而且您还必须处理这种情况其中被装饰的类自己已经有一个 __new__() 方法。
您可能认为您可以编写一个 __del__() 方法来允许在没有对现有实例的引用时创建一个新的单例。这是行不通的,因为始终存在对实例的类内部引用(例如,Fire._instance),因此永远不会调用 __del__()。一旦你有了一个单例,它就会一直存在。如果您在关闭旧的单例后想要一个新的单例,您可能实际上并不想要单例,而是想要别的东西。 context manager有可能。
在某些情况下可以重新实例化的“单例”对我来说是非常奇怪和意外的行为,我建议不要这样做。尽管如此,如果那是您真正想要的,您可以在 close_() 方法中执行 self.__class__._instance = None。或者您可以编写一个单独的方法来执行此操作。它看起来很丑,这很合适,因为它很丑。 :-)
我认为您的第三个问题也源于这样一个事实,即您希望单例在您调用 close_() 之后以某种方式消失,而您还没有对该行为进行编程。
