Ограничение создания объекта в одноэлементном классе с помощью конструктора в Python - PullRequest
2 голосов
/ 26 апреля 2019

Я в середине написания одноэлементного класса для одного из требований в Python. Я мог бы понять, что я не могу ограничить создание объекта этого класса, если какой-то клиент сначала делает вызов с конструктором, прежде чем использовать метод класса singleton (означает метод, который возвращает единственный экземпляр класса).

Что я имею в виду под этим? Позвольте мне объяснить это с помощью некоторого фрагмента кода. Считайте, что у меня есть один из следующих примеров:


import threading
class MyClass:

    __instance = None

    def __init__(self):

        if self.__instance:
            raise ValueError("Already exist")


    @classmethod
    def getInstance(cls):

        lock = threading.Lock()

        with lock:
            if cls.__instance == None:
                cls.__instance = MyClass()
            return cls.__instance

    def printObjInfo(self,obj):
        print(id(obj))


if __name__ == '__main__':

    ob4 = MyClass()
    ob4.printObjInfo(ob4)

    obj1 = MyClass.getInstance()
    obj1.printObjInfo(obj1)

    obj2 = MyClass.getInstance()
    obj2.printObjInfo(obj2)

    # Create a thread
    obj3 = MyClass.getInstance()
    obj3.printObjInfo(obj3)
    t1 = threading.Thread(target=obj3.printObjInfo, args=(obj3))

Если бы я запустил этот фрагмент кода выше, я получил бы результат, как показано ниже:

44824952 - Object created by constructor.
44826240 - Object created by getInstance() method.
44826240 - Object created by getInstance() method.
44826240 - Object created by getInstance() method.

Следует отметить одну вещь - если кто-то вызывает конструктор после вызова метода getInstance () , тогда мы можем легко ограничить создание другого объекта. Но если он будет вызван первым, мы не сможем это контролировать.

Теперь проблема в том, что: 1) я не могу поставить какие-либо дополнительные условия в __init __ (), чтобы никто не вызывал это или 2) не могу сделать мой конструктор приватным - могу ли я?

Я нашел здесь ссылку - Программа для ограничения создания объектов в Python но не уверен, как мы можем ограничить создание самого первого объекта. Есть ли лучший способ, который позволяет вам это сделать?

Есть мысли или ссылки?

Спасибо.

Ответы [ 2 ]

1 голос
/ 26 апреля 2019

Вы можете переопределить __new__ вместо:

class Singleton:

    _instance = None

    def __init__(self, arg):
        self.arg = arg

    def __new__(cls, arg):
        if cls._instance is None:
            cls._instance = object.__new__(cls)
            return cls._instance

        else:
            return cls._instance

print(Singleton(None))
print(Singleton(None))
print(Singleton(None))

Выход:

<__main__.Singleton object at 0x1207fa550>
<__main__.Singleton object at 0x1207fa550>
<__main__.Singleton object at 0x1207fa550>

Преимущество этого заключается в том, что существует единый унифицированный интерфейс для создания и получения единственного экземпляра Singleton: конструктор.

Обратите внимание, что если вы не напишите свое собственное расширение C или что-то подобное, вы никогда не сможете создать настоящий синглтон в Python:

print(object.__new__(Singleton))
print(object.__new__(Singleton))
print(object.__new__(Singleton))

Выход:

<__main__.Singleton object at 0x120806ac8>
<__main__.Singleton object at 0x1207d5b38>
<__main__.Singleton object at 0x1207d5198>
0 голосов
/ 26 апреля 2019

Потокобезопасная синглтонная версия на основе ответа gmds

from multiprocessing.dummy import Pool as ThreadPool 
import threading
import time

class ThreadSafeSingleton:
    __instance = None
    __lock = threading.Lock()

    def __init__(self, *args, **kwargs):
        self.args = args
        self.kwargs = kwargs

    def __str__(self):
        return 'id: {}, args: {}, kwargs: {}'.format(id(self), self.args, self.kwargs)

    def __new__(cls, *args, **kwargs):
        with cls.__lock:
            if cls.__instance is None:
                # sleep just simulate heavy class initialization  !!
                # process under concurrency circumstance          !!
                time.sleep(1)
                print('created')
                cls.__instance = super(ThreadSafeSingleton, cls).__new__(cls)
            return cls.__instance


class ThreadUnsafeSingleton:
    __instance = None

    def __init__(self, *args, **kwargs):
        self.args = args
        self.kwargs = kwargs

    def __str__(self):
        return 'id: {}, args: {}, kwargs: {}'.format(id(self), self.args, self.kwargs)

    def __new__(cls, *args, **kwargs):
        if cls.__instance is None:
            time.sleep(1)
            print('created')
            cls.__instance = super(ThreadUnsafeSingleton, cls).__new__(cls)
        return cls.__instance


def create_safe(*args, **kwargs):
    obj = ThreadSafeSingleton(*args, **kwargs)
    print(obj)


def create_unsafe(*args, **kwargs):
    obj = ThreadUnsafeSingleton(*args, **kwargs)
    print(obj)


if __name__ == '__main__':
    pool = ThreadPool(4)
    print('---- test thread safe singleton  ----')
    pool.map(create_safe, range(10))

    print('\n---- test thread unsafe singleton  ----')
    pool.map(create_unsafe, range(10))

Вывод:

---- test thread safe singleton  ----
created
id: 4473136352, args: (0,), kwargs: {}
id: 4473136352, args: (1,), kwargs: {}
id: 4473136352, args: (2,), kwargs: {}
id: 4473136352, args: (4,), kwargs: {}
id: 4473136352, args: (5,), kwargs: {}
id: 4473136352, args: (3,), kwargs: {}
id: 4473136352, args: (6,), kwargs: {}
id: 4473136352, args: (7,), kwargs: {}
id: 4473136352, args: (8,), kwargs: {}
id: 4473136352, args: (9,), kwargs: {}

---- test thread unsafe singleton  ----
created
id: 4473136968, args: (0,), kwargs: {}
created
created
created
id: 4473136968, args: (4,), kwargs: {}
id: 4473137024, args: (2,), kwargs: {}
id: 4473137080, args: (1,), kwargs: {}
id: 4473137136, args: (3,), kwargs: {}
id: 4473137136, args: (5,), kwargs: {}
id: 4473137136, args: (7,), kwargs: {}
id: 4473137136, args: (6,), kwargs: {}
id: 4473137136, args: (8,), kwargs: {}
id: 4473137136, args: (9,), kwargs: {}
...