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


945

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

मेरे पास कई वर्ग हैं जो एकल बन जाएंगे (मेरा उपयोग-मामला एक लकड़हारे के लिए है, लेकिन यह महत्वपूर्ण नहीं है)। जब मैं केवल विरासत या सजावट कर सकता हूं, तो मुझे जोड़ा गया गम के साथ कई वर्गों को अव्यवस्थित करने की इच्छा नहीं है।

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


विधि 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

पेशेवरों

  • डेकोरेटर्स एक तरह से एडिटिव होते हैं जो अक्सर कई इनहेरिटेंस की तुलना में अधिक सहज होते हैं।

विपक्ष

  • जबकि MyClass () का उपयोग करके बनाई गई वस्तुएँ सही सिंगलटन ऑब्जेक्ट्स होंगी, MyClass स्वयं एक फंक्शन है, क्लास नहीं है, इसलिए आप इसमें से क्लास के तरीके नहीं कह सकते। 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: एक मेटाक्लस

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__और उस वर्ग को उपवर्गित नहीं कर सकते हैं जिसकी आपको आवश्यकता है __init__

विधि 5: एक मॉड्यूल

एक मॉड्यूल फ़ाइल singleton.py

पेशेवरों

  • सरल जटिल से बेहतर है

विपक्ष


12
एक और तीन तकनीक: इसके बजाय एक मॉड्यूल का उपयोग करें (अक्सर - आम तौर पर, मुझे लगता है - यह पायथन के लिए एक अधिक उपयुक्त पैटर्न है लेकिन यह उस पर थोड़ा निर्भर करता है कि आप इसके साथ क्या कर रहे हैं); एक एकल उदाहरण बनाएं और इसके बजाय इससे निपटें ( foo.xया यदि आप Foo.xइसके बजाय जोर देते हैं Foo().x); वर्ग विशेषताओं और स्थिर / वर्ग विधियों ( Foo.x) का उपयोग करें।
क्रिस मॉर्गन

10
@ क्रिसमोरगन: यदि आप केवल क्लास / स्टैटिक विधियों का उपयोग करने जा रहे हैं, तो वास्तव में एक क्लास बनाने से परेशान न हों।
कैट प्लस प्लस

2
@ कैट: प्रभाव समान है, हालांकि एक वैश्विक चर बनाने के पीछे कारण कुछ भी हो सकते हैं, जिसमें कोई भी बेहतर नहीं जानना शामिल है। कोई एकल क्यों बनाता है? अगर आपको पूछना है तो आपको यहां नहीं होना चाहिए। यह खोजकर्ता न केवल अधिक अजगर है, बल्कि रखरखाव को और अधिक सरल बनाता है। हां सिंगलटन ग्लोबल्स के लिए सिंटैक्टिक शुगर हैं, लेकिन फिर कक्षाएं पूरी तरह से भद्दे सामानों के लिए सिंटैक्टिक शुगर हैं और मुझे नहीं लगता कि कोई भी आपको बताएगा कि आप उनके बिना हमेशा बेहतर हैं।
theheadofabroom

14
@BiggAl: सिंगलेट्स पाइथोनिक नहीं हैं , चाहे आप उन्हें कैसे भी लागू करें। वे सबसे अच्छी तरह से एक दोषपूर्ण डिजाइन का संकेत हैं।
कैट प्लस प्लस

8
एंटी-साइनलेट्स सेंटीमेंट कार्गो की प्रोग्रामिंग सबसे खराब है। सुनने वाले लोगों के साथ भी (कुछ वास्तव में पढ़ने के लिए परेशान) "गोटो बयान हानिकारक माना जाता है" और लगता है कि गोटो संदर्भ की परवाह किए बिना खराब कोड का संकेत हैं।
हज्जाज़मैन

जवाबों:


657

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

मैं विधि # 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)

करने ifमें बयान Singleton.__call__

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

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

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

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

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

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

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

Python2 और 3 दोनों में काम करने वाले कुछ को लिखना थोड़ा अधिक जटिल योजना का उपयोग करना है। चूंकि metaclasses आमतौर पर प्रकार के उपवर्गों हैं type, यह गतिशील अपने metaclass के रूप में यह साथ रन टाइम पर एक मध्यस्थ के आधार वर्ग बनाने और उसके बाद का उपयोग करने से एक का उपयोग करने के लिए संभव है कि जनता के baseclass के रूप में 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)वापस आ जाएगा True

सुधार

किसी अन्य विषय पर, आपने शायद पहले ही इस पर ध्यान दिया है, लेकिन आपके मूल पोस्ट में बेस क्लास कार्यान्वयन गलत है। _instancesजरूरतों को किए जाने की कक्षा पर संदर्भित , आप उपयोग करने की आवश्यकता super()है या आप कर रहे हैं recursing , और __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(), क्योंकि वे recurse होगा। इसका मतलब है कि आपकी कक्षा को अनुकूलित नहीं किया जा सकता है __new__, और उन सभी वर्गों से प्राप्त नहीं किया जा सकता है __init__जिन्हें उन पर कॉल करने की आवश्यकता है।

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

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

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

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

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

यहाँ http://googletesting.blogspot.com/2008/08/root-cause-of-singhonsons.html से एक उद्धरण है :

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

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


5
नहीं, सिंगलटन कभी अच्छे नहीं होते। लॉगिंग एक वैश्विक होने के लिए एक अच्छा उम्मीदवार हो सकता है (जैसा कि वे भयानक हैं), लेकिन निश्चित रूप से सिंगलटन नहीं।
कैट प्लस प्लस

11
को देखो googletesting.blogspot.com/2008/08/... । यह आम तौर पर एंटी-सिंगलटन (अच्छे कारण के लिए) होता है, लेकिन इसका एक अच्छा स्पष्टीकरण है कि क्यों बिना साइड इफेक्ट्स के अपरिवर्तनीय एकल और सिंगलटन में समान समस्याएं नहीं हैं, अगर आप सावधान रहें। मैं अपनी पोस्ट के अंत में इसे थोड़ा उद्धृत करने जा रहा हूं।
एजीएफ़

4
सिंगल के साथ मेरी समस्या "केवल एक उदाहरण" का बेवकूफाना आधार है। धागा सुरक्षा समस्याओं का वह और टन। और निर्भरता छिपाना। ग्लोबल्स खराब हैं, और सिंगलटन अधिक समस्या वाले ग्लोबल्स हैं।
कैट प्लस प्लस

4
@ गायन के लिए बहुत अच्छे उपयोग हैं। हार्डवेयर मॉड्यूल की आलसी तात्कालिकता (विशेष रूप से एकल थ्रेडेड अनुप्रयोगों में) उनमें से एक है (लेकिन थ्रेड सेफ सिंगलटन भी मौजूद हैं)।
पॉल मंटा

3
@ मेटाक्लॉट __new__में एक अतिक्रमण तब होता है जब वर्ग नया होता है - जब इसे परिभाषित किया जाता है, तब नहीं जब उदाहरण नया होगा। क्लास को कॉल करना ( MyClass()) वह ऑपरेशन है जिसे आप ओवरराइड करना चाहते हैं, न कि क्लास की परिभाषा। यदि आप वास्तव में यह समझना चाहते हैं कि पायथन कैसे काम करता है, तो आप जो सबसे अच्छा काम कर सकते हैं (उसका उपयोग करने के अलावा रखें) docs.python.org/reference/datamodel.html पढ़ा जाता हैमेटाक्लासेस पर एक अच्छा संदर्भ eli.thegreenplace.net/2011/08/14/python-metaclasses-by-example है । सिंगलेट्स पर एक अच्छा लेख इस उत्तर में लिंक किए गए Google ब्लॉग से श्रृंखला है।
एएफएफ

91
class Foo(object):
     pass

some_global_variable = Foo()

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


14
आपने क्यों कहा "एकल का उपयोग न करें"? कोई कारण?
Alcott

3
यह काम नहीं करेगा अगर सिंगलटन को चुना जाना है। आपके द्वारा दिए गए उदाहरण का उपयोग करना:s = some_global_variable; str = pickle.dumps(s); s1 = pickle.loads(str); print s is s1; # False
dividebyzero

4
@dividebyzero: isपॉइंटर समानता के लिए ऑपरेटर परीक्षण करता है। मुझे आश्चर्य होगा --- इसे बग कहने के लिए --- अगर pickle.loadsकिसी नए बनाए गए के संदर्भ के बजाय पहले से मौजूद वस्तु का संदर्भ दिया जाए। इस प्रकार, परीक्षण करना कि क्या s is s1आप एकल के रूप में मॉड्यूल का उपयोग करने की उपयुक्तता के बारे में कुछ नहीं बताते हैं।
जोनास कोल्कर

1
@ JonasKölker pickle.loads()पहले से ही ऐसा करता है, उदाहरण के लिए boolऔर NoneTypepickle.loads(pickle.dumps(False)) is FalseपैदावारTrue
दान Passaro

2
@ leo-the-manic: उचित बिंदु; हालाँकि, यह पिथॉन का एक साइड इफेक्ट है जो ऑब्जेक्ट्स को इंटर्न करता है True, Falseऔर Noneपीछे के कोड से इसका कोई लेना-देना नहीं है pickle.loads। इसके अलावा, यह केवल पढ़ने के लिए केवल वस्तुओं के लिए सुरक्षित है। यदि pickle.loadsपहले से मौजूद एक मॉडिफाइड ऑब्जेक्ट जैसे कि एक मॉड्यूल के लिए एक संदर्भ लौटाया जाए - तो यह एक बग होगा। (और इसलिए मैं अपने निहितार्थ से खड़ा हुआ हूं कि डिवाइडबीज़रो का कोड उदाहरण कुछ भी साबित नहीं करता है।)
जोनास कोल्कर

69

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


11
तो क्या आप के साथ समाप्त होता है ... एक वर्ग नहीं है। आप इसे एक वर्ग के रूप में उपयोग नहीं कर सकते, आप इस पर अन्य वर्गों को आधार नहीं बना सकते, आप आयात वाक्यविन्यास का उपयोग करते हैं, और अचानक आप OOP के सभी लाभों को खो देते हैं ...
theheadofabroom

16
यदि आप इस पर अन्य वर्गों को आधार बना सकते हैं, तो यह एक एकल नहीं हो सकता है। आप व्युत्पन्न वर्ग में से एक बना सकते हैं, लेकिन यह भी एक आधार वर्ग है, लेकिन व्युत्पन्न वर्ग भी आधार का सदस्य है, और आपके पास दो आधार हैं, जिन्हें आप उपयोग करने वाले हैं?
सिंगलएनजेशन इलिमिनेशन

यह मॉड्यूल में काम नहीं करता है। मेरे "मुख्य" मॉड्यूल में मैंने एक मूल्य निर्धारित किया है। मैं फिर इसे एक अन्य मॉड्यूल और इसके शून्य में संदर्भित करता हूं। आह।
पॉल केनजोरा

1
@PaulKenjora आपके कोड में कोई त्रुटि होनी चाहिए। यदि आप एक मॉड्यूल में एक वैश्विक चर को परिभाषित करते हैं, जब आप इसे किसी अन्य मॉड्यूल से एक्सेस करते हैं तो इसका मूल्य होना चाहिए।
वार्वारियुक

इसका मूल्य है लेकिन जब मैं इसे बदलता हूं तो यह संरक्षित नहीं होता है। मैंने काम किया एक मॉड्यूल में गुणों के साथ एक वर्ग का उपयोग करके। ग्लोबल्स के रूप में सरल प्रकार मेरे लिए काम नहीं करते थे (जैसे ही गुंजाइश बदली उन्होंने मान खो दिया)।
पॉल केंजोरा

29

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

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

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

my_singleton = My_Singleton()

काम में लाना:

from mysingleton import my_singleton
my_singleton.foo()

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


4
ज्यादातर सच है, लेकिन कभी-कभी यह पर्याप्त नहीं है। उदाहरण के लिए, मेरे पास DEBUG स्तर पर कई वर्गों के त्वरित लॉग इन करने की आवश्यकता के साथ एक परियोजना है। मुझे कमांड लाइन विकल्प स्टार्टअप पर पार्स करने की आवश्यकता है ताकि उपयोगकर्ता-निर्दिष्ट लॉगिंग स्तर सेट करने से पहले उन कक्षाओं को त्वरित किया जा सके। मॉड्यूल स्तर के इंस्टेंटिएशन उस समस्या को बनाते हैं। यह संभव है कि मैं ऐप को सावधानीपूर्वक संरचना कर सकता हूं ताकि उन सभी वर्गों को आयात न किया जाए जब तक कि सीएलआई प्रसंस्करण नहीं किया जाता है, लेकिन मेरे ऐप की प्राकृतिक संरचना "सिंगलेट्स खराब हैं" के लिए हठधर्मी पालन की तुलना में अधिक महत्वपूर्ण है, क्योंकि वे हो सकते हैं काफी सफाई से किया।
CryingCyclops

यदि आप my_singleton को पैच करते समय अपने कोड का परीक्षण करते हैं, तो क्या यह संभव होगा? चूँकि यह my_singleton कुछ अन्य मॉड्यूल में इंस्टेंट हो सकता है।
नवीन

@ नवीन - my_singleton एक एकल वस्तु है। यदि आप "पैच" करते हैं, तो यह परिवर्तन भविष्य के सभी संदर्भों को प्रभावित करेगा, यहां तक ​​कि अन्य मॉड्यूल में भी।
एलन डाइक

16

यहाँ आपके लिए वन-लाइनर है:

singleton = lambda c: c()

यहां बताया गया है कि आप इसका उपयोग कैसे करते हैं:

@singleton
class wat(object):
    def __init__(self): self.x = 1
    def get_x(self): return self.x

assert wat.get_x() == 1

आपकी वस्तु का उत्साह पूर्वक मूल्यांकन हो जाता है। यह वही हो सकता है या नहीं हो सकता है जो आप चाहते हैं।


5
-1। यह बहुत बुरा और मूर्खतापूर्ण है। आप ऑब्जेक्ट के रूप में उपयोग करने के लिए एक वर्ग को परिभाषित नहीं करते हैं। अब आप की तरह बहुत बदसूरत किया जा रहा बिना वर्ग का उपयोग कर सकते type(wat)या wat.__class__। यदि आप वास्तव में इसे प्राप्त करना चाहते हैं, तो बेहतर ढंग से कक्षा को परिभाषित करें और तुरंत इसे समाप्त कर दें, डेकोरेटर के साथ गड़बड़ करने की कोई आवश्यकता नहीं है।
0xc0de

2
आपको एकल के वर्ग का उपयोग करने की आवश्यकता क्यों है? बस सिंगलटन ऑब्जेक्ट का उपयोग करें ..
टॉली

1
यह सिंगलटन पैटर्न नहीं है , इसलिए IMO फ़ंक्शन को अलग नाम दिया जाना चाहिए।
जिंजरप्लस निष्कर्ष

8
विकिपीडिया: "सिंगलटन पैटर्न एक डिज़ाइन पैटर्न है जो किसी कक्षा को एक वस्तु के लिए तत्काल रोक देता है"। मैं कहूंगा कि मेरा समाधान बस यही करता है। ठीक है, मुझे लगता है कि एक कर सकता है wat2 = type(wat)(), लेकिन यह अजगर है, हम सभी वयस्कों और सभी को सहमति दे रहे हैं। आप गारंटी नहीं दे सकते हैं कि केवल एक ही उदाहरण होगा, लेकिन आप गारंटी दे सकते हैं कि यदि लोग एक दूसरे को बनाते हैं, तो यह बदसूरत दिखाई देगा और - यदि वे सभ्य हैं, लोगों को समझ रहे हैं - जैसे एक चेतावनी संकेत। मैं क्या खो रहा हूँ?
जोनास कोल्कर

7

स्टैक ओवरफ्लो प्रश्न देखें पायथन में एकल को परिभाषित करने का एक सरल, सुरुचिपूर्ण तरीका है? कई समाधानों के साथ।

मैं अजगर में डिजाइन पैटर्न पर एलेक्स मार्टेली की बातचीत देखने की जोरदार सिफारिश करूंगा: भाग 1 और भाग 2 । विशेष रूप से, भाग 1 में वह एकल / साझा राज्य वस्तुओं के बारे में बात करता है।


2
हालांकि यह वास्तव में मेरे सवाल का जवाब नहीं है, आपके द्वारा इंगित संसाधन बहुत उपयोगी हैं। मैं आपको
theheadofabroom

3

यहाँ सिंगलेट्स का अपना कार्यान्वयन है। आपको बस क्लास को सजाना है; सिंगलटन प्राप्त करने के लिए, आपको फिर 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)

2
यह सच नहीं सिंगलटन है। सच्चे सिंगलटन में SingletonList = Singleton(list).Instance(); print(SingletonList is type(SingletonList)())प्रिंट करना चाहिए True; अपने कोड प्रिंट्स के साथFalse
GingerPlusPlus 22

@GingerPlusPlus मुझे कुछ सीमाओं के बारे में पता था, लेकिन आपके द्वारा इंगित किए गए नहीं। इसका उल्लेख करने के लिए धन्यवाद। दुर्भाग्य से, इस समय मेरे पास इसके समाधान के लिए समय नहीं है।
पॉल मंटा

2

विधि 3 बहुत साफ-सुथरी लगती है, लेकिन यदि आप चाहते हैं कि आपका प्रोग्राम पायथन 2 और पायथन 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

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


निश्चित रूप से यह एक मेटाक्लस नहीं है - python3 में माईक्लास के निर्माण के लिए एक मेटाक्लास का उपयोग करने के लिए आप क्या करेंगेclass MyClass(metaclass=Singleton)
theheadofabroom

Mikewatkins.ca लिंक (प्रभावी रूप से) टूट गया है।
पीटर मोर्टेंसन

1

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

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

गुणस्थान क्या है _sealed? जहाँ तक मुझे लगता है कि यह कुछ भी नहीं करता है? कुछ मुझे इस बारे में परेशान कर रहा है जो कहता है कि इसे अच्छा प्रदर्शन नहीं करना चाहिए ... मैं इस सप्ताह के अंत में कुछ तुलनात्मक परीक्षण चलाऊंगा।
theheadofabroom

_sealed सुनिश्चित करता है कि आपका init केवल एक बार चलाया जाता है; मुझे ऐसा कोई भी बिंदु दिखाई नहीं देता है कि वह नियमित फ़ंक्शन-डेकोरेटर की तुलना में खराब प्रदर्शन क्यों करे - फ़ंक्शन को प्रति कक्षा केवल एक बार निष्पादित किया जाता है, और एक नया विरासत में मिला वर्ग
गार्ड

BTW, आपके संपादन में ऐसे टैब होते हैं जो इंडेंट को तोड़ते हैं आप यह भी कहते हैं कि 'हम 2 कक्षाएं बना रहे हैं' - क्या आपका मतलब है कि हम '1 अतिरिक्त क्लास' बना रहे हैं?
गार्ड

हां एक अतिरिक्त कक्षा का मेरा मतलब है। मैं __init__हर बार इसे शुरू करने के लिए बुलाया जाने वाले सामान को शामिल करने का इरादा रखता हूं । बस एक सरल 'class.method में आरंभीकृत किया जा रहा है'। इंडेंटेशन के लिए - आपने टैब और रिक्त स्थान का उपयोग किया है - मैंने इसे सबसे अधिक निर्धारित किया है, लेकिन लगता है कि यदि आप इसे प्राप्त करना चाहते हैं तो एक चूक हो गई है (बस एडिट लॉग की जांच करें)
theheadofabroom

पुन : init : बेशक यह आपके ऊपर है, मैंने सिर्फ अन्य भाषाओं में सिंगलटन व्यवहार की नकल करने की कोशिश की है, जहां कंस्ट्रक्टर कोड (जो बिल्कुल init नहीं है , लेकिन इसके अर्थ में बहुत करीब है) केवल एक बार कहा जाता है यदि आप चाहते हैं कि init हो हर बार कॉल किया जाता है, बस सभी संदर्भों को _sealed re space / tabs - अच्छी तरह से मार दें, फिर मेरे ईमेक को फिक्सिंग की आवश्यकता है। वैसे भी, ऊपर सही संस्करण है
गार्ड

1

इस बारे में कैसा है:

def singleton(cls):
    instance=cls()
    cls.__new__ = cls.__call__= lambda cls: instance
    cls.__init__ = lambda self: None
    return instance

एक वर्ग पर एक डेकोरेटर के रूप में उपयोग करें जो एक सिंगलटन होना चाहिए। ऐशे ही:

@singleton
class MySingleton:
    #....

यह singleton = lambda c: c()एक अन्य उत्तर में डेकोरेटर के समान है । अन्य समाधान की तरह, एकमात्र उदाहरण में वर्ग ( MySingleton) का नाम है । हालांकि, इस समाधान से आप अभी भी कक्षा से (वास्तव में एकमात्र उदाहरण प्राप्त कर सकते हैं) "कर" बना सकते हैं MySingleton()। यह आपको अतिरिक्त आवृत्ति बनाने से भी रोकता है type(MySingleton)()(यह भी एक ही उदाहरण देता है)।


आप इसे एक वस्तु के रूप में उपयोग करने के लिए एक वर्ग को परिभाषित नहीं करते हैं।
0xc0de

1
हर बार जब आप कॉल करते हैं type(MySingleton)(), कॉल किया MySingleton.__init__()जाता है और ऑब्जेक्ट को कई बार इनिशियलाइज़ किया जाता है; आप इसे cls.__init__ = lambda self: passअपने में लिखकर ठीक कर सकते हैं singleton। इसके अलावा, ओवरराइडिंग cls.__call__व्यर्थ लगता है, और यहां तक ​​कि हानिकारक - __call__इस संदर्भ में परिभाषित किया जाता है जब आप कॉल करते हैं MySingleton(any, list, of, arguments), तब जब आप कॉल करते हैं type(MySingleton)(any, list, of, arguments)
जिंजरप्लस निष्कर्ष

@GingerPlusPlus, यह इंगित करने के लिए धन्यवाद कि किस करते __init__()समय फिर से कॉल किया जाता है type(MySingleton)()। आपके द्वारा प्रस्तावित समाधान (जोड़कर cls.__init__ = lambda self: pass) एक सिंटैक्स त्रुटि देता है, क्योंकि लैम्ब्डा अभिव्यक्ति के अंतिम भाग को एक अभिव्यक्ति की आवश्यकता है, न कि एक बयान। हालांकि, cls.__init__ = lambda self: Noneकाम जोड़ना , इसलिए मैंने अपने जवाब में इसे जोड़ा।
टॉलि

1
@GingerPlusPlus, के उपयोग के बारे में __call__। मेरा इरादा दोनों को बनाने type(MySingleton)()और MySingleton()उदाहरण वापस करने का था । तो यह वही कर रहा है जो मैं चाहता था। आप MySingleton के बारे में सोच सकते हैं या तो सिंगलटन का प्रकार या सिंगलटन का उदाहरण (या दोनों)।
टोलि

1

मैं रिंग में अपना टॉस करूंगा। यह एक साधारण सज्जाकार है।

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.

मेरे विचार से इसके कुछ अन्य समाधान हैं:

  • यह स्पष्ट और संक्षिप्त है (मेरी आँख के लिए; डी)।
  • इसकी कार्रवाई पूरी तरह से ध्वस्त है। के कार्यान्वयन के बारे में आपको किसी एक चीज को बदलने की जरूरत नहीं है YourClass। इसमें आपकी कक्षा के लिए मेटाक्लास का उपयोग करने की आवश्यकता नहीं है (ध्यान दें कि ऊपर दिए गए मेटाक्लास कारखाने पर हैं, "वास्तविक" वर्ग नहीं)।
  • यह कुछ भी बंदर-पैचिंग पर भरोसा नहीं करता है।
  • यह कॉल करने वालों के लिए पारदर्शी है:
    • कॉलर्स अभी भी आयात करते हैं YourClass, यह एक वर्ग की तरह दिखता है (क्योंकि यह है), और वे इसे सामान्य रूप से उपयोग करते हैं। फ़ैक्टर्स फ़ंक्शन के लिए कॉलर्स को अनुकूलित करने की आवश्यकता नहीं है।
    • क्या YourClass()इंस्टेंटिअट्स अभी भी YourClassआपके द्वारा लागू किया गया सही उदाहरण है , किसी प्रकार का प्रॉक्सी नहीं है, इसलिए इसके परिणामस्वरूप होने वाले दुष्प्रभावों की कोई संभावना नहीं है।
    • isinstance(instance, YourClass) और इसी तरह के संचालन अभी भी उम्मीद के मुताबिक काम करते हैं (हालांकि इस बिट को abc की आवश्यकता है इसलिए पायथन <2.6)।

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

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

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

उस सभी ने कहा, मैं सामान्य सलाह से सहमत हूं कि अगर आपको लगता है कि आपको इन चीजों में से एक की आवश्यकता है, तो आपको वास्तव में एक पल के लिए रुकना चाहिए और यदि आप वास्तव में करते हैं तो खुद से पूछें। 99% समय, YAGNI।


1
  • एक ही कक्षा के उदाहरणों के एक से अधिक संख्या के लिए है, लेकिन चाहता है ही अगर आर्ग या kwargs अलग हैं, एक का उपयोग कर सकते हैं इस
  • पूर्व।
    1. यदि आपके पास serialसंचार से निपटने के लिए एक वर्ग है , और एक उदाहरण बनाने के लिए आप एक तर्क के रूप में सीरियल पोर्ट भेजना चाहते हैं, तो पारंपरिक दृष्टिकोण के साथ काम नहीं करेगा
    2. उपर्युक्त डेकोरेटर का उपयोग करके, वर्ग के कई उदाहरण बना सकते हैं यदि आर्ग अलग हैं।
    3. उसी आर्ग के लिए, डेकोरेटर उसी उदाहरण को वापस करेगा जो पहले से ही बनाया गया है।
>>> from decorators import singleton
>>>
>>> @singleton
... class A:
...     def __init__(self, *args, **kwargs):
...         pass
...
>>>
>>> a = A(name='Siddhesh')
>>> b = A(name='Siddhesh', lname='Sathe')
>>> c = A(name='Siddhesh', lname='Sathe')
>>> a is b  # has to be different
False
>>> b is c  # has to be same
True
>>>

0

टॉली के उत्तर के आधार पर कोड ।

#decorator, modyfies new_cls
def _singleton(new_cls):
    instance = new_cls()                                              #2
    def new(cls):
        if isinstance(instance, cls):                                 #4
            return instance
        else:
            raise TypeError("I can only return instance of {}, caller wanted {}".format(new_cls, cls))
    new_cls.__new__  = new                                            #3
    new_cls.__init__ = lambda self: None                              #5
    return new_cls


#decorator, creates new class
def singleton(cls):
    new_cls = type('singleton({})'.format(cls.__name__), (cls,), {} ) #1
    return _singleton(new_cls)


#metaclass
def meta_singleton(name, bases, attrs):
    new_cls = type(name, bases, attrs)                                #1
    return _singleton(new_cls)

स्पष्टीकरण:

  1. नया वर्ग बनाएं, जो विरासत में दिया गया हो cls
    ( clsउदाहरण के लिए कोई व्यक्ति चाहे तो इसे संशोधित नहीं कर सकता singleton(list))

  2. उदाहरण बनाएँ। ओवरराइड __new__करने से पहले यह इतना आसान है।

  3. अब, जब हमने आसानी से उदाहरण बनाया है, तो __new__पहले से परिभाषित विधि का उपयोग करते हुए ओवरराइड करता है ।
  4. फ़ंक्शन instanceतभी वापस आता है जब वह कॉल करने वाले से अपेक्षा करता है, अन्यथा उठता है TypeError
    स्थिति तब पूरी नहीं होती है जब कोई सजाया वर्ग से विरासत में मिलने का प्रयास करता है।

  5. यदि __new__()कोई उदाहरण देता है cls, तो नए उदाहरण की __init__()विधि को लागू किया जाएगा__init__(self[, ...]) , जैसे कि स्वयं नया उदाहरण है और शेष तर्क वही हैं जो पारित किए गए थे __new__()

    instanceपहले से ही आरंभिक है, इसलिए फ़ंक्शन फ़ंक्शन को __init__कुछ नहीं करने के साथ बदल देता है ।

इसे ऑनलाइन काम करते हुए देखें


0

यह फैब द्वारा उत्तर के समान है, लेकिन बिल्कुल समान नहीं है।

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

तो मेरा सुझाव सिर्फ यह है:

class Elvis():
    def __init__(self):
        if hasattr(self.__class__, 'instance'):
            raise Exception()
        self.__class__.instance = self
        # initialisation code...

    @staticmethod
    def the():
        if hasattr(Elvis, 'instance'):
            return Elvis.instance
        return Elvis()

यह instanceउपयोगकर्ता कोड द्वारा कंस्ट्रक्टर या फ़ील्ड के उपयोग से इंकार नहीं करता है :

if Elvis() is King.instance:

... अगर आपको पता है कि Elvisअभी तक बनाया नहीं गया है, और यह Kingहै कि ।

लेकिन यह उपयोगकर्ताओं को सार्वभौमिक रूप से विधि का उपयोग करने के लिए प्रोत्साहित करता हैthe :

Elvis.the().leave(Building.the())

इसे पूरा करने के लिए आप __delattr__()एक अपवाद को उठाने के लिए ओवरराइड भी कर सकते हैं यदि हटाने का प्रयास किया जाता है instance, और ओवरराइड किया जाता है __del__()ताकि यह एक अपवाद उठाएं (जब तक कि हमें पता नहीं है कि कार्यक्रम समाप्त हो रहा है ...)

और सुधार


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

try:
    # This is jython-specific
    from synchronize import make_synchronized
except ImportError:
    # This should work across different python implementations
    def make_synchronized(func):
        import threading
        func.__lock__ = threading.Lock()

        def synced_func(*args, **kws):
            with func.__lock__:
                return func(*args, **kws)

        return synced_func

class Elvis(object): # NB must be subclass of object to use __new__
    instance = None

    @classmethod
    @make_synchronized
    def __new__(cls, *args, **kwargs):
        if cls.instance is not None:
            raise Exception()
        cls.instance = object.__new__(cls, *args, **kwargs)
        return cls.instance

    def __init__(self):
        pass
        # initialisation code...

    @classmethod
    @make_synchronized
    def the(cls):
        if cls.instance is not None:
            return cls.instance
        return cls()

नोट के अंक:

  1. यदि आप python2.x में ऑब्जेक्ट से उपवर्ग नहीं करते हैं, तो आपको एक पुरानी शैली की क्लास मिलेगी, जो उपयोग नहीं करता है __new__
  2. जब सजाने __new__आप @classmethod साथ सजाने चाहिए या __new__एक अबाध उदाहरण विधि हो जाएगा
  3. यह संभवतः मेटाक्लस के उपयोग के माध्यम से सुधारा जा सकता है, क्योंकि यह theसंभवतः आपको एक श्रेणी-स्तरीय संपत्ति बनाने की अनुमति देगा , संभवतः इसका नामinstance

हालांकि यह सिंगलटन पैटर्न की थोड़ी अलग व्याख्या है, मुझे पूरा यकीन है कि यह अभी भी मान्य है, हालांकि मुझे __newइसके बजाय __ का उपयोग करने के लिए लुभाया जा सकता है __init__, क्योंकि यह विशुद्ध रूप से वर्गीय गुणों पर कार्य करता है और यह कुछ समय के लिए दूसरा उदाहरण होने से रोकता है। इस अंतर और विधि 2 के बीच का अंतर यह है कि क्या एकल उदाहरण को लौटाने या एक अपवाद को बढ़ाने की तुलना में अधिक बार इनिशियलाइज़ करने की कोशिश की जा रही है। मुझे लगता है कि मैं खुश हूं कि या तो सिंगलटन पैटर्न को संतुष्ट करें, एक का उपयोग करना आसान है, जबकि दूसरा अधिक स्पष्ट है कि यह एक सिंगलटन है।
theheadofabroom

स्पष्ट रूप __init__से उपवर्ग को रोकने में वर्ग के नाम का उपयोग , लेकिन जब यह चीजों को आसान बनाता है, तो इसकी आवश्यकता नहीं है
theheadofabroom

धन्यवाद ... आह हां, अपवाद से पहले एक क्षणिक दूसरा उदाहरण फेंक दिया जाता है। मैंने ऐसा संशोधित किया __init__है कि उम्मीद है कि यह उप-विषयनीय होना चाहिए ...
माइक कृंतक

कूल, theसंभवतः इसी तरह के कारणों के लिए एक वर्ग विधि होने से लाभ उठा सकते थे
theheadofabroom

हाँ तुम सही हो। तब आपके पास एक SuperElvis सबक्लास सिंगलटन हो सकता है और (उदाहरण के लिए) एक ImaginaryElvis सबक्लास सिंगलटन ... और वे सह-अस्तित्व में हो सकते हैं। अतिरिक्त विचार देखें। कृपया मेरे कोड में सुधार करने के लिए स्वतंत्र महसूस करें।
माइक कृंतक

0

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

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

यह तब तक काम कर सकता है जब तक आपकी कक्षा को किसी अन्य मॉड्यूल में आयात नहीं किया गया है ...
अरन-फे

सच। यदि आप वर्ग की शुरुआत की लागत का भुगतान कर सकते हैं तो आप इससे बचने के लिए कक्षा की परिभाषा के तुरंत बाद माइक्लास () चला सकते हैं।
पोलोवेज़ुल

0

यदि आपको सिंग्लटन के उदाहरण के आलसी आरंभीकरण की आवश्यकता नहीं है, तो निम्न आसान और थ्रेड-सुरक्षित होना चाहिए:

class A:
    instance = None
    # Methods and variables of the class/object A follow
A.instance = A()

यह तरीका Aमॉड्यूल इम्पोर्ट पर इनिशियलाइज्ड एक सिंगलटन है।


0

शायद मैं सिंगलटन पैटर्न को याद कर रहा हूं, लेकिन मेरा समाधान यह सरल और व्यावहारिक (पायथोनिक?) है। यह कोड दो लक्ष्यों को पूरा करता है

  1. का उदाहरण बनाइए Fooहर जगह (वैश्विक) तक पहुंच कायम ।
  2. केवल एक ही उदाहरण Fooमौजूद हो सकता है।

यह कोड है।

#!/usr/bin/env python3

class Foo:
    me = None

    def __init__(self):
        if Foo.me != None:
            raise Exception('Instance of Foo still exists!')

        Foo.me = self


if __name__ == '__main__':
    Foo()
    Foo()

उत्पादन

Traceback (most recent call last):
  File "./x.py", line 15, in <module>
    Foo()
  File "./x.py", line 8, in __init__
    raise Exception('Instance of Foo still exists!')
Exception: Instance of Foo still exists!

0

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

import builtins

# -----------------------------------------------------------------------------
# So..... you would expect that a class would be "global" in scope, however
#   when different modules use this,
#   EACH ONE effectively has its own class namespace.  
#   In order to get around this, we use a metaclass to intercept
#   "new" and provide the "truly global metaclass instance" if it already exists

class MetaConfig(type):
    def __new__(cls, name, bases, dct):
        try:
            class_inst = builtins.CONFIG_singleton

        except AttributeError:
            class_inst = super().__new__(cls, name, bases, dct)
            builtins.CONFIG_singleton = class_inst
            class_inst.do_load()

        return class_inst

# -----------------------------------------------------------------------------

class Config(metaclass=MetaConfig):

    config_attr = None

    @classmethod
    def do_load(cls):
        ...<load-cfg-from-file>...

-1

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

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__विधि को हटा दें । जैसा कि मैं पायथन के लिए नया हूं (और सामान्य रूप से ओओपी) मुझे उम्मीद है कि कोई मुझे सीधे सेट करेगा कि यह एक भयानक दृष्टिकोण क्यों है?


2
यह एक भयानक दृष्टिकोण क्यों है? जब आप एक और सिंगलटन क्लास बनाना चाहते हैं, तो आपको कॉपी और पेस्ट करना होगा __new__अपने आप को दोहराएं नहीं
जिंजरप्लस २५

इसके अलावा, आपका नया *argsऔर क्यों **kwargs, और फिर उनके साथ कुछ भी नहीं करता है? उन्हें में पारित dict.__new__इस तरह से: dict.__new__(cls, *args, **kwargs)
GingerPlusPlus

__init__हर बार कक्षा को बुलाए जाने पर यह विधि को बुलाएगा । यदि आपकी __init__विधि ने वास्तव में कुछ किया है, तो आप समस्या को नोटिस करेंगे। जब भी आप करते हैं SomeSingleton(), तो आपके सिंगलटन की स्थिति __init__विधि द्वारा रीसेट हो जाती है ।
अरन-फे

-2

यह एकल गायन को लागू करने का मेरा पसंदीदा तरीका है:

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

1
यह कड़ाई से एक सिंगलटन नहीं है क्योंकि यह कक्षा के एक से अधिक उदाहरणों के अस्तित्व के लिए अनुमति देता है। कक्षा को प्रारंभिक बनाने में असमर्थ बनाने के लिए एक सुधार होगा, और सभी वर्ग विधियां वर्ग विशेषताओं पर कार्य
करेंगी

-2

यह उत्तर संभवतः वह नहीं है जिसकी आप तलाश कर रहे हैं। मैं इस मायने में एक सिंगलटन चाहता था कि केवल उस वस्तु की अपनी पहचान हो, तुलना करने के लिए। मेरे मामले में इसे सेंटिनल वैल्यू के रूप में इस्तेमाल किया जा रहा था । जिसके लिए उत्तर बहुत सरल है, किसी भी वस्तु को बनाएं mything = object()और अजगर की प्रकृति से, केवल उस चीज की अपनी पहचान होगी।

#!python
MyNone = object()  # The singleton

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

मैंने सीखा है कि मॉड्यूल वास्तव में कई बार आयात किए जा सकते हैं, ऐसे में यह केवल एक स्थानीय सिंगलटन है, जो वास्तव में किसी भी क्षमता में एक सिंगलटन नहीं है।
थोरसुमोनर

क्या आप विस्तार से बता सकते हैं कि एक मॉड्यूल को कई बार कैसे आयात किया जा सकता है? केवल समय मैंने देखा है कि जब मॉड्यूल लोड होने के दौरान अपवाद होता है, तो उपयोगकर्ता अभी भी बाद में मॉड्यूल लोड कर सकता है, फिर भी साइड इफेक्ट पहले से ही हो सकता है, इसलिए कुछ कार्यों को दूसरी बार निष्पादित किया जा सकता है।
स्लीपब्लांक

एक बार एक मॉड्यूल पूरी तरह से लोड हो जाने के बाद, मुझे इस मॉड्यूल को फिर से चलाने का कोई रास्ता नहीं दिखता है, इसके अलावा दुभाषिया का उपयोग करके इसे करने evalया करने में दुविधा में पड़ने के अलावाimportlib.reload
स्लीपब्लैंक

-3

यह समाधान मॉड्यूल स्तर पर कुछ नाम स्थान के प्रदूषण का कारण बनता है (केवल एक के बजाय तीन परिभाषाएं), लेकिन मुझे इसका पालन करना आसान लगता है।

मैं ऐसा कुछ लिखने में सक्षम होना चाहूंगा (आलसी इनिशियलाइज़ेशन), लेकिन दुर्भाग्य से कक्षाएं अपनी परिभाषाओं के शरीर में उपलब्ध नहीं हैं।

# wouldn't it be nice if we could do this?
class Foo(object):
    instance = None

    def __new__(cls):
        if cls.instance is None:
            cls.instance = object()
            cls.instance.__class__ = Foo
        return cls.instance

चूंकि यह संभव नहीं है, इसलिए हम इनिशियलाइज़ेशन और स्टैटिक इंस्टेंस को तोड़ सकते हैं

उत्सुक शुरुआत:

import random


class FooMaker(object):
    def __init__(self, *args):
        self._count = random.random()
        self._args = args


class Foo(object):
    def __new__(self):
        return foo_instance


foo_instance = FooMaker()
foo_instance.__class__ = Foo

आलसी आरंभीकरण:

उत्सुक शुरुआत:

import random


class FooMaker(object):
    def __init__(self, *args):
        self._count = random.random()
        self._args = args


class Foo(object):
    def __new__(self):
        global foo_instance
        if foo_instance is None:
            foo_instance = FooMaker()
        return foo_instance


foo_instance = None
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.