Синглтон-класс Python - PullRequest
       25

Синглтон-класс Python

1 голос
/ 19 декабря 2011

Я пишу набор тестов для Firefox 5.1 и Selenn WebDrive v.2 на OS X 10,6 с Python 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

Я могуне понимаю этого.

Ответы [ 2 ]

6 голосов
/ 19 декабря 2011

Ваш декоратор, кажется, работает нормально для меня, поскольку создает один экземпляр класса, поэтому я не вижу вашей проблемы # 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__() никогда не вызывается.Если у вас есть синглтон, он останется здесь.Если вам нужен новый синглтон после того, как вы закроете старый, , вы, вероятно, на самом деле не хотите синглтон , а скорее что-то еще. менеджер контекста может быть возможен.

"Синглтон", который может быть восстановлен при определенных обстоятельствах, будет для меня действительно странным и неожиданным поведением, и я бы посоветовал противЭто.Тем не менее, если вы действительно этого хотите, вы можете сделать self.__class__._instance = None в вашем close_() методе.Или вы можете написать отдельный метод для этого.Это выглядит некрасиво, что уместно, потому что некрасиво.: -)

Я думаю, что ваш третий вопрос также возникает из-за того, что вы ожидаете, что синглтон каким-то образом исчезнет после того, как вы вызовете close_(), когда вы не запрограммировали это поведение.

2 голосов
/ 19 декабря 2011

Проблема в том, что вы используете этот синглтон-класс в качестве декоратора. Это вовсе не декоратор, так что использовать его, как никто другой, не имеет смысла.

Декоратор должен фактически вернуть декорированный объект - обычно это функция, но в вашем случае это класс. Вы просто возвращаете функцию. Очевидно, что когда вы пытаетесь использовать его в isinstance, Fire больше не относится к классу.

...