python - पायथन में एक सिंगलटन बनाना




singleton decorator (13)

यह सवाल इस बात की चर्चा के लिए नहीं है कि सिंगलटन डिज़ाइन पैटर्न वांछनीय है या नहीं, एक विरोधी पैटर्न है, या किसी भी धार्मिक युद्ध के लिए, लेकिन चर्चा करने के लिए कि इस पैटर्न को पायथन में इस तरह से सबसे अच्छा तरीके से कैसे लागू किया जाता है, जो कि अधिकांश पायथनिक है। इस उदाहरण में मैं 'अधिकांश पायथनिक' को परिभाषित करने के लिए परिभाषित करता हूं कि यह 'कम से कम आश्चर्य के सिद्धांत' का पालन करता है

मेरे पास कई कक्षाएं हैं जो सिंगलेट बन जाएंगी (मेरा उपयोग-मामला लॉगर के लिए है, लेकिन यह महत्वपूर्ण नहीं है)। जब मैं आसानी से उत्तराधिकारी या सजा सकता हूं तो मैं अतिरिक्त गम्फ के साथ कई कक्षाओं को अव्यवस्थित नहीं करना चाहता हूं।

सर्वोत्तम तरीके:

विधि 1: एक सजावटी

def singleton(class_):
    instances = {}
    def getinstance(*args, **kwargs):
        if class_ not in instances:
            instances[class_] = class_(*args, **kwargs)
        return instances[class_]
    return getinstance

@singleton
class MyClass(BaseClass):
    pass

पेशेवरों

  • सजावटी इस तरह से additive हैं जो अक्सर एकाधिक विरासत से अधिक सहज है।

विपक्ष

  • जबकि MyClass () का उपयोग करके बनाई गई ऑब्जेक्ट्स सच सिंगलटन ऑब्जेक्ट्स होंगी, MyClass स्वयं एए फ़ंक्शन है, क्लास नहीं, इसलिए आप क्लास विधियों को कॉल नहीं कर सकते हैं। m = MyClass(); n = MyClass(); o = type(n)(); m = MyClass(); n = MyClass(); o = type(n)(); फिर m == n && m != o && n != o

विधि 2: एक बेस क्लास

class Singleton(object):
    _instance = None
    def __new__(class_, *args, **kwargs):
        if not isinstance(class_._instance, class_):
            class_._instance = object.__new__(class_, *args, **kwargs)
        return class_._instance

class MyClass(Singleton, BaseClass):
    pass

पेशेवरों

  • यह एक असली कक्षा है

विपक्ष

  • एकाधिक विरासत - eugh! __new__ को दूसरी बेस क्लास से विरासत के दौरान ओवरराइट किया जा सकता है? एक को जरूरी से ज्यादा सोचना पड़ता है।

विधि 3: एक metaclass

class Singleton(type):
    _instances = {}
    def __call__(cls, *args, **kwargs):
        if cls not in cls._instances:
            cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
        return cls._instances[cls]

#Python2
class MyClass(BaseClass):
    __metaclass__ = Singleton

#Python3
class MyClass(BaseClass, metaclass=Singleton):
    pass

पेशेवरों

  • यह एक असली कक्षा है
  • ऑटो जादुई विरासत को कवर करता है
  • इसका उचित उद्देश्य के लिए __metaclass__ उपयोग करता है (और मुझे इसके बारे में पता चला)

विपक्ष

  • क्या वहां पर कोई?

विधि 4: सजावटी एक ही नाम के साथ एक कक्षा लौट रहा है

def singleton(class_):
    class class_w(class_):
        _instance = None
        def __new__(class_, *args, **kwargs):
            if class_w._instance is None:
                class_w._instance = super(class_w,
                                    class_).__new__(class_,
                                                    *args,
                                                    **kwargs)
                class_w._instance._sealed = False
            return class_w._instance
        def __init__(self, *args, **kwargs):
            if self._sealed:
                return
            super(class_w, self).__init__(*args, **kwargs)
            self._sealed = True
    class_w.__name__ = class_.__name__
    return class_w

@singleton
class MyClass(BaseClass):
    pass

पेशेवरों

  • यह एक सच्ची कक्षा है
  • ऑटो जादुई विरासत को कवर करता है

विपक्ष

  • क्या प्रत्येक नई कक्षा बनाने के लिए कोई ओवरहेड नहीं है? यहां हम प्रत्येक कक्षा के लिए दो कक्षाएं बना रहे हैं जिन्हें हम सिंगलटन बनाना चाहते हैं। हालांकि यह मेरे मामले में ठीक है, मुझे चिंता है कि यह स्केल नहीं हो सकता है। निस्संदेह बहस का विषय है कि क्या इस पैटर्न को स्केल करना बहुत आसान है ...
  • _sealed विशेषता का बिंदु क्या है
  • super() का उपयोग कर बेस क्लास पर उसी नाम के तरीकों को कॉल नहीं कर सकते क्योंकि वे रिकर्स करेंगे। इसका मतलब है कि आप __new__ कस्टमाइज़ नहीं कर सकते हैं और एक वर्ग को उपclass नहीं कर सकते हैं जिसके लिए आपको __init__ पर कॉल करने की आवश्यकता है।

मेटाक्लास का प्रयोग करें

मैं विधि # 2 की अनुशंसा करता हूं, लेकिन आप बेस क्लास की तुलना में मेटाक्लास का उपयोग करना बेहतर कर सकते हैं। नमूना कार्यान्वयन यहां दिया गया है:

class Singleton(type):
    _instances = {}
    def __call__(cls, *args, **kwargs):
        if cls not in cls._instances:
            cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
        return cls._instances[cls]

class Logger(object):
    __metaclass__ = Singleton

या पायथन 3 में

class Logger(metaclass=Singleton):
    pass

यदि आप हर बार क्लास कहलाते हैं तो __init__ को चलाने के लिए, जोड़ना चाहते हैं

        else:
            cls._instances[cls].__init__(*args, **kwargs)

Singleton.__call__ में if कथन में Singleton.__call__

मेटाक्लास के बारे में कुछ शब्द। एक मेटाक्लास एक वर्ग की कक्षा है ; यानी, एक वर्ग अपने मेटाक्लास का एक उदाहरण है । आपको पायथन में ऑब्जेक्ट का मेटाक्लास type(obj) । सामान्य नई शैली के वर्ग प्रकार के type । ऊपर दिए गए कोड में Logger टाइप class 'your_module.Singleton' , जैसे कि Logger का (केवल) उदाहरण टाइप class 'your_module.Logger' । जब आप लॉगर Logger() साथ लॉगर कहते हैं, तो पाइथन पहले Logger , Singleton के मेटाक्लास को पूछता है, क्या करना है, उदाहरण के निर्माण को पूर्व-खाली करने की इजाजत देता है। यह प्रक्रिया Python के समान है जो क्लास से पूछती है कि __getattr__ को कॉल करके क्या करना है जब आप __getattr__ करके इसके गुणों में से किसी एक को संदर्भित करते हैं।

एक मेटाक्लास अनिवार्य रूप से निर्णय लेता है कि कक्षा की परिभाषा क्या है और उस परिभाषा को कैसे कार्यान्वित किया जाए। उदाहरण के लिए देखें http://code.activestate.com/recipes/498149/ , जो अनिवार्य रूप से मेटाक्लास का उपयोग करके पायथन में सी-शैली struct पुन: प्रयास करता है। धागा पाइथन में मेटाक्लास के लिए आपके (ठोस) उपयोग-मामले क्या हैं? कुछ उदाहरण भी प्रदान करता है, वे आमतौर पर घोषणात्मक प्रोग्रामिंग से संबंधित होते हैं, खासकर ओआरएम में उपयोग किए जाने वाले।

इस स्थिति में, यदि आप अपना विधि # 2 का उपयोग करते हैं, और उप-वर्ग एक __new__ विधि को परिभाषित करता है, तो जब भी आप SubClassOfSingleton() कॉल SubClassOfSingleton() तो इसे निष्पादित किया जाएगा - क्योंकि यह संग्रहीत उदाहरण लौटने वाली विधि को कॉल करने के लिए ज़िम्मेदार है। मेटाक्लास के साथ, इसे केवल एक बार बुलाया जाएगा , जब एकमात्र उदाहरण बनाया जाएगा। आप क्लास को कॉल करने के लिए इसका मतलब क्या अनुकूलित करना चाहते हैं, जिसे इसके प्रकार से तय किया जाता है।

आम तौर पर, एक सिंगलटन को लागू करने के लिए मेटाक्लास का उपयोग करना समझ में आता है। एक सिंगलटन विशेष है क्योंकि केवल एक बार बनाया जाता है, और मेटाक्लास एक वर्ग के निर्माण को अनुकूलित करने का तरीका है। एक मेटाक्लास का उपयोग करके आपको अन्य तरीकों से सिंगलटन क्लास परिभाषाओं को कस्टमाइज़ करने की आवश्यकता होने पर अधिक नियंत्रण मिलता है।

आपके सिंगलेट्स को एकाधिक विरासत की आवश्यकता नहीं होगी (क्योंकि मेटाक्लास बेस क्लास नहीं है), लेकिन बनाए गए वर्ग के उप-वर्गों के लिए जो एकाधिक विरासत का उपयोग करते हैं, आपको यह सुनिश्चित करना होगा कि सिंगलटन क्लास मेटाक्लास के साथ पहला / बाएं वाला है जो फिर से परिभाषित करता है __call__ यह एक मुद्दा होने की संभावना नहीं है। उदाहरण का उदाहरण उदाहरण के नामस्थान में नहीं है, इसलिए यह गलती से इसे ओवरराइट नहीं करेगा।

आप यह भी सुनेंगे कि सिंगलटन पैटर्न "एकल उत्तरदायित्व सिद्धांत" का उल्लंघन करता है - प्रत्येक वर्ग को केवल एक ही चीज़ करना चाहिए। इस तरह आपको एक चीज को गड़बड़ करने की चिंता करने की ज़रूरत नहीं है, अगर कोड को दूसरे को बदलने की ज़रूरत है, क्योंकि वे अलग हैं और encapsulated हैं। मेटाक्लास कार्यान्वयन इस परीक्षण को पास करता है । मेटाक्लास पैटर्न को लागू करने के लिए ज़िम्मेदार है और निर्मित वर्ग और उप-वर्गों को यह पता नहीं होना चाहिए कि वे सिंगलेट हैंविधि # 1 इस परीक्षण में विफल रहता है, जैसा कि आपने "MyClass स्वयं एए फ़ंक्शन है, कक्षा नहीं है, इसलिए आप क्लास विधियों को कॉल नहीं कर सकते हैं।"

पायथन 2 और 3 संगत संस्करण

कुछ ऐसा लिखना जो Python2 और 3 दोनों में काम करता है, थोड़ा और जटिल योजना का उपयोग करने की आवश्यकता है। चूंकि मेटाक्लास आमतौर पर टाइप type उप-वर्ग होते हैं, इसलिए इसके उपयोग के लिए गतिशील रूप से मध्यस्थ आधार वर्ग को इसके मेटाक्लास के रूप में उपयोग करना संभव है और फिर इसे सार्वजनिक Singleton बेस क्लास के बेसक्लास के रूप में उपयोग करना संभव है । जैसा कि आगे दिखाया गया है, करने की तुलना में व्याख्या करना कठिन है:

# works in Python 2 & 3
class _Singleton(type):
    """ A metaclass that creates a Singleton base class when called. """
    _instances = {}
    def __call__(cls, *args, **kwargs):
        if cls not in cls._instances:
            cls._instances[cls] = super(_Singleton, cls).__call__(*args, **kwargs)
        return cls._instances[cls]

class Singleton(_Singleton('SingletonMeta', (object,), {})): pass

class Logger(Singleton):
    pass

इस दृष्टिकोण का एक विडंबनात्मक पहलू यह है कि यह मेटाक्लास लागू करने के लिए उप-वर्गीकरण का उपयोग कर रहा है। एक संभावित लाभ यह है कि, शुद्ध isinstance(inst, Singleton) विपरीत, isinstance(inst, Singleton) True हो जाएगा।

सुधार

किसी अन्य विषय पर, आपने शायद पहले ही यह देखा है, लेकिन आपकी मूल पोस्ट में बेस क्लास कार्यान्वयन गलत है। _instances को कक्षा में संदर्भित करने की आवश्यकता है, आपको super() का उपयोग करने की आवश्यकता है या आप __new__ कर रहे हैं, और __new__ वास्तव में एक स्थिर विधि है जिसे आपको क्लास विधि को कक्षा में नहीं पारित करना है , क्योंकि वास्तविक वर्ग नहीं है इसे तब तक बनाया गया जब इसे कहा जाता है। इन सभी चीजों को मेटाक्लास कार्यान्वयन के लिए भी सच होगा।

class Singleton(object):
  _instances = {}
  def __new__(class_, *args, **kwargs):
    if class_ not in class_._instances:
        class_._instances[class_] = super(Singleton, class_).__new__(class_, *args, **kwargs)
    return class_._instances[class_]

class MyClass(Singleton):
  pass

c = MyClass()

सजावट एक कक्षा लौट रहा है

मैं मूल रूप से एक टिप्पणी लिख रहा था लेकिन यह बहुत लंबा था, इसलिए मैं इसे यहां जोड़ दूंगा। विधि # 4 अन्य सजावट संस्करण से बेहतर है, लेकिन यह सिंगलटन के लिए आवश्यक से अधिक कोड है, और यह स्पष्ट नहीं है कि यह क्या करता है।

कक्षा से मुख्य समस्याएं अपनी खुद की बेस क्लास हैं। सबसे पहले, क्या यह वर्ग अजीब नहीं है कि कक्षा एक समान नाम के साथ लगभग समान वर्ग का उप-वर्ग हो जो केवल __class__ विशेषता में मौजूद हो? इसका यह भी अर्थ है कि आप किसी भी तरीके को परिभाषित नहीं कर सकते हैं जो उसी आधार की विधि को अपने बेस क्लास पर super() साथ कॉल करता है क्योंकि वे रिकर्स करेंगे। इसका मतलब है कि आपकी कक्षा __new__ कस्टमाइज़ नहीं कर सकती है, और उन वर्गों से प्राप्त नहीं हो सकती है जिन्हें __init__ पर बुलाया जाता है।

सिंगलटन पैटर्न का उपयोग कब करें

आपका उपयोग केस एक सिंगलटन का उपयोग करने के इच्छुक उदाहरणों में से एक है। आप एक टिप्पणी में कहते हैं "मुझे लॉगिंग करने के लिए हमेशा सिंगलेट्स के लिए एक प्राकृतिक उम्मीदवार लग रहा है।" तुम बिल्कुल सही हो

जब लोग कहते हैं कि सिंगलेट्स खराब हैं, तो सबसे आम कारण यह है कि वे निहित साझा राज्य हैं । जबकि ग्लोबल वेरिएबल्स और टॉप-स्तरीय मॉड्यूल आयात स्पष्ट रूप से साझा किए गए राज्य हैं, अन्य ऑब्जेक्ट्स जो चारों ओर पारित होते हैं, आमतौर पर तत्काल होते हैं। यह दो अपवादों के साथ एक अच्छा मुद्दा है

पहला, और जो विभिन्न स्थानों पर उल्लिखित होता है, वह तब होता है जब सिंगलटन स्थिर होते हैं। वैश्विक स्थिरांक, विशेष रूप से enums का उपयोग व्यापक रूप से स्वीकार किया जाता है, और साधु माना जाता है क्योंकि इससे कोई फर्क नहीं पड़ता कि कोई भी उपयोगकर्ता किसी अन्य उपयोगकर्ता के लिए उन्हें गड़बड़ नहीं कर सकता है । यह निरंतर सिंगलटन के लिए भी उतना ही सच है।

दूसरा अपवाद, जो कम उल्लेख किया गया है, विपरीत है - जब सिंगलटन केवल डेटा सिंक होता है , डेटा स्रोत नहीं (प्रत्यक्ष या परोक्ष रूप से)। यही कारण है कि लॉगर्स सिंगलेट्स के लिए "प्राकृतिक" उपयोग की तरह महसूस करते हैं। चूंकि विभिन्न उपयोगकर्ता लॉगर्स को अन्य तरीकों से बदल नहीं रहे हैं, वास्तव में साझा स्थिति नहीं है । यह सिंगलटन पैटर्न के खिलाफ प्राथमिक तर्क को अस्वीकार करता है, और कार्य के लिए उपयोग की आसानी के कारण उन्हें उचित विकल्प बनाता है।

http://googletesting.blogspot.com/2008/08/root-cause-of-singletons.html से उद्धरण यहां दिया गया है:

अब, एक तरह का सिंगलटन है जो ठीक है। वह एक सिंगलटन है जहां सभी पहुंच योग्य वस्तुएं अपरिवर्तनीय हैं। यदि सिंगलटन की तुलना में सभी ऑब्जेक्ट्स अपरिवर्तनीय हैं तो कोई वैश्विक स्थिति नहीं है, क्योंकि सब कुछ स्थिर है। लेकिन इस तरह के सिंगलटन को परिवर्तनीय में बदलना इतना आसान है, यह बहुत फिसलन ढलान है। इसलिए, मैं इन सिंगलेट्स के खिलाफ भी हूं, न कि क्योंकि वे बुरे हैं, लेकिन क्योंकि उनके लिए बुरा जाना बहुत आसान है। (एक साइड नोट के रूप में जावा गणना सिर्फ इस तरह के सिंगलेट हैं। जब तक आप अपनी गणना में राज्य नहीं डालते हैं, तो आप ठीक हैं, इसलिए कृपया नहीं।)

अन्य प्रकार के सिंगलेट्स, जो अर्ध-स्वीकार्य हैं वे हैं जो आपके कोड के निष्पादन को प्रभावित नहीं करते हैं, उनके पास "दुष्प्रभाव" नहीं हैं। लॉगिंग सही उदाहरण है। यह सिंगलेट्स और वैश्विक राज्य से भरा हुआ है। यह स्वीकार्य है (जैसा कि इसमें आपको चोट नहीं पहुंचीगी) क्योंकि आपका एप्लिकेशन किसी भी अलग व्यवहार से व्यवहार नहीं करता है या नहीं दिया गया है या नहीं। यहां दी गई जानकारी एक तरफ बहती है: आपके आवेदन से लॉगर में। यहां तक ​​कि सोचा कि लॉगर्स वैश्विक स्थिति हैं क्योंकि आपके आवेदन में लॉगर्स से कोई जानकारी बहती नहीं है, लॉगर्स स्वीकार्य हैं। यदि आप चाहते हैं कि आपका परीक्षण लॉग इन हो रहा है, तो आपको अभी भी अपने लॉगर को इंजेक्ट करना चाहिए, लेकिन सामान्य रूप से लॉगर्स राज्य से भरे होने के बावजूद हानिकारक नहीं हैं।


आपको शायद पाइथन में सिंगलटन की आवश्यकता नहीं है। बस मॉड्यूल में अपने सभी डेटा और कार्यों को परिभाषित करें और आपके पास एक डी-फैक्टो सिंगलटन है।

यदि आपको वास्तव में एक सिंगलटन कक्षा है तो मैं इसके साथ जाऊंगा:

class My_Singleton(object):
    def foo(self):
        pass

my_singleton = My_Singleton()

काम में लाना:

from mysingleton import my_singleton
my_singleton.foo()

जहां mysingleton.py आपका फ़ाइल नाम है कि My_Singleton को परिभाषित किया गया है। यह काम करता है क्योंकि पहली बार फ़ाइल आयात होने के बाद, पायथन कोड को फिर से निष्पादित नहीं करता है।


एक मॉड्यूल का प्रयोग करें। यह केवल एक बार आयात किया जाता है। इसमें कुछ वैश्विक चर परिभाषित करें - वे सिंगलटन के 'गुण' होंगे। कुछ फ़ंक्शन जोड़ें - सिंगलटन की 'विधियां'।


एक लाइनर (मुझे गर्व नहीं है, लेकिन यह नौकरी करता है):

class Myclass:
  def __init__(self):
      # do your stuff
      globals()[type(self).__name__] = lambda: self # singletonify

खैर, मॉड्यूल-स्तरीय वैश्विक होने पर सामान्य पायथनिक सुझाव से सहमत होने के अलावा, इसके बारे में:

def singleton(class_):
    class class_w(class_):
        _instance = None
        def __new__(class2, *args, **kwargs):
            if class_w._instance is None:
                class_w._instance = super(class_w, class2).__new__(class2, *args, **kwargs)
                class_w._instance._sealed = False
            return class_w._instance
        def __init__(self, *args, **kwargs):
            if self._sealed:
                return
            super(class_w, self).__init__(*args, **kwargs)
            self._sealed = True
    class_w.__name__ = class_.__name__
    return class_w

@singleton
class MyClass(object):
    def __init__(self, text):
        print text
    @classmethod
    def name(class_):
        print class_.__name__

x = MyClass(111)
x.name()
y = MyClass(222)
print id(x) == id(y)

आउटपुट है:

111     # the __init__ is called only on the 1st time
MyClass # the __name__ is preserved
True    # this is actually the same instance

मुझे याद नहीं है कि मुझे यह समाधान कहां मिला, लेकिन मुझे लगता है कि यह मेरे गैर-पायथन-विशेषज्ञ दृष्टिकोण से सबसे अधिक 'सुरुचिपूर्ण' है:

class SomeSingleton(dict):
    __instance__ = None
    def __new__(cls, *args,**kwargs):
        if SomeSingleton.__instance__ is None:
            SomeSingleton.__instance__ = dict.__new__(cls)
        return SomeSingleton.__instance__

    def __init__(self):
        pass

    def some_func(self,arg):
        pass

मुझे यह क्यों पसंद है? कोई सजावट नहीं, कोई मेटा वर्ग नहीं, कोई एकाधिक विरासत नहीं ... और यदि आप तय करते हैं कि आप इसे सिंगलटन नहीं चाहते हैं, तो बस __new__ विधि को हटा __new__ । जैसा कि मैं पाइथन (और आम तौर पर ओओपी) के लिए नया हूं, मुझे उम्मीद है कि कोई मुझे सीधे सेट करेगा कि यह एक भयानक दृष्टिकोण क्यों है?


यहां सिंगलेट्स का अपना कार्यान्वयन है। आपको बस कक्षा को सजाने के लिए करना है; सिंगलटन प्राप्त करने के लिए, आपको Instance विधि का उपयोग करना होगा। यहां एक उदाहरण दिया गया है:

   @Singleton
   class Foo:
       def __init__(self):
           print 'Foo created'

   f = Foo() # Error, this isn't how you get the instance of a singleton

   f = Foo.Instance() # Good. Being explicit is in line with the Python Zen
   g = Foo.Instance() # Returns already created instance

   print f is g # True

और यहां कोड है:

class Singleton:
    """
    A non-thread-safe helper class to ease implementing singletons.
    This should be used as a decorator -- not a metaclass -- to the
    class that should be a singleton.

    The decorated class can define one `__init__` function that
    takes only the `self` argument. Other than that, there are
    no restrictions that apply to the decorated class.

    To get the singleton instance, use the `Instance` method. Trying
    to use `__call__` will result in a `TypeError` being raised.

    Limitations: The decorated class cannot be inherited from.

    """

    def __init__(self, decorated):
        self._decorated = decorated

    def Instance(self):
        """
        Returns the singleton instance. Upon its first call, it creates a
        new instance of the decorated class and calls its `__init__` method.
        On all subsequent calls, the already created instance is returned.

        """
        try:
            return self._instance
        except AttributeError:
            self._instance = self._decorated()
            return self._instance

    def __call__(self):
        raise TypeError('Singletons must be accessed through `Instance()`.')

    def __instancecheck__(self, inst):
        return isinstance(inst, self._decorated)

विधि 3 बहुत साफ दिखता है, लेकिन यदि आप चाहते हैं कि आपका प्रोग्राम Python 2 और Python 3 दोनों में चलाना चाहे, तो यह काम नहीं करता है। पाइथन संस्करण के परीक्षणों के साथ अलग-अलग प्रकारों की सुरक्षा भी विफल हो जाती है, क्योंकि पायथन 3 संस्करण पायथन 2 में सिंटैक्स त्रुटि देता है।

माइक वाटकिन्स के लिए धन्यवाद: http://mikewatkins.ca/2008/11/29/python-2-and-3-metaclasses/ । यदि आप प्रोग्राम को पाइथन 2 और पायथन 3 दोनों में काम करना चाहते हैं, तो आपको कुछ ऐसा करने की ज़रूरत है:

class Singleton(type):
    _instances = {}
    def __call__(cls, *args, **kwargs):
        if cls not in cls._instances:
            cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
        return cls._instances[cls]

MC = Singleton('MC', (object), {})

class MyClass(MC):
    pass    # Code for the class implementation

मुझे लगता है कि असाइनमेंट में 'ऑब्जेक्ट' को 'बेस क्लास' के साथ प्रतिस्थापित करने की आवश्यकता है, लेकिन मैंने कोशिश नहीं की है (मैंने सचित्र के रूप में कोड की कोशिश की है)।



I'll toss mine into the ring. It's a simple decorator.

from abc import ABC

def singleton(real_cls):

    class SingletonFactory(ABC):

        instance = None

        def __new__(cls, *args, **kwargs):
            if not cls.instance:
                cls.instance = real_cls(*args, **kwargs)
            return cls.instance

    SingletonFactory.register(real_cls)
    return SingletonFactory

# Usage
@singleton
class YourClass:
    ...  # Your normal implementation, no special requirements.

Benefits I think it has over some of the other solutions:

  • It's clear and concise (to my eye ;D).
  • Its action is completely encapsulated. You don't need to change a single thing about the implementation of YourClass . This includes not needing to use a metaclass for your class (note that the metaclass above is on the factory, not the "real" class).
  • It doesn't rely on monkey-patching anything.
  • It's transparent to callers:
    • Callers still simply import YourClass , it looks like a class (because it is), and they use it normally. No need to adapt callers to a factory function.
    • What YourClass() instantiates is still a true instance of the YourClass you implemented, not a proxy of any kind, so no chance of side effects resulting from that.
    • isinstance(instance, YourClass) and similar operations still work as expected (though this bit does require abc so precludes Python <2.6).

One downside does occur to me: classmethods and staticmethods of the real class are not transparently callable via the factory class hiding it. I've used this rarely enough that I've never happen to run into that need, but it would be easily rectified by using a custom metaclass on the factory that implements __getattr__() to delegate all-ish attribute access to the real class.

A related pattern I've actually found more useful (not that I'm saying these kinds of things are required very often at all) is a "Unique" pattern where instantiating the class with the same arguments results in getting back the same instance. Ie a "singleton per arguments". The above adapts to this well and becomes even more concise:

def unique(real_cls):

    class UniqueFactory(ABC):

        @functools.lru_cache(None)  # Handy for 3.2+, but use any memoization decorator you like
        def __new__(cls, *args, **kwargs):
            return real_cls(*args, **kwargs)

    UniqueFactory.register(real_cls)
    return UniqueFactory

All that said, I do agree with the general advice that if you think you need one of these things, you really should probably stop for a moment and ask yourself if you really do. 99% of the time, YAGNI.


This answer is likely not what you're looking for. I wanted a singleton in the sense that only that object had its identity, for comparison to. In my case it was being used as a Sentinel Value . To which the answer is very simple, make any object mything = object() and by python's nature, only that thing will have its identity.

#!python
MyNone = object()  # The singleton

for item in my_list:
    if item is MyNone:  # An Example identity comparison
        raise StopIteration

This is my preferred way of implementing singletons:

class Test(object):
    obj = None

    def __init__(self):
        if Test.obj is not None:
            raise Exception('A Test Singleton instance already exists')
        # Initialization code here

    @classmethod
    def get_instance(cls):
        if cls.obj is None:
            cls.obj = Test()
        return cls.obj

    @classmethod
    def custom_method(cls):
        obj = cls.get_instance()
        # Custom Code here

class Foo(object):
     pass

some_global_variable = Foo()

मॉड्यूल केवल एक बार आयात किए जाते हैं, बाकी सब कुछ खत्म हो रहा है। सिंगलेट का उपयोग न करें और ग्लोबल्स का उपयोग न करने का प्रयास करें।





metaclass