我正在使用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
我无法理解这一点。
答案 0 :(得分: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
如果您仍然希望使用装饰器,最简单的方法是在装饰器中创建一个中间类并返回:
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
您可以将所需的行为注入到现有的类对象中,但在我看来,这是不必要的复杂,因为它需要(在Python 2.x中)创建方法包装器,并且您还必须处理这种情况正在装饰的类本身已经有__new__()
方法。
您可能认为可以编写__del__()
方法,以便在没有对现有实例的引用时创建新的单例。这不起作用,因为始终存在对实例的类内部引用(例如,Fire._instance
),因此永远不会调用__del__()
。一旦你有一个单身人士,它就会留下来。如果你在关闭旧单身后想要一个新的单身,你可能实际上并不想要一个单身而是另外一些。可能有context manager。
在某些情况下可以重新实例化的“单身人士”,对我来说,实际上是非常奇怪和意想不到的行为,我会反对它。尽管如此,如果这是你真正想要的,你可以在你的self.__class__._instance = None
方法中close_()
。或者您可以编写一个单独的方法来执行此操作。它看起来很难看,这很合适,因为它是丑陋的。 : - )
我认为你的第三个问题也来自这样一个事实:当你没有编程那个行为时,你预计单身就会以某种方式消失{@ 1}}。
答案 1 :(得分:2)
问题是您使用该单例类作为装饰器。它根本不是装饰器,所以像它一样使用它是没有意义的。
装饰器需要实际返回装饰对象 - 通常是一个函数,但在你的情况下,类。你刚刚返回一个函数。很明显,当您尝试在isinstance
中使用它时,Fire
不再引用某个类。