मेटाक्लास का उपयोग करें
मैं विधि # 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)
। सामान्य नई शैली की कक्षाएं प्रकार की होती हैं type
। Logger
ऊपर दिए गए कोड में प्रकार होगा 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 से एक उद्धरण है :
अब, एक प्रकार का सिंगलटन है जो ठीक है। यह एक सिंगलटन है जहां सभी उपलब्ध वस्तुएँ अपरिवर्तनीय हैं। यदि सभी वस्तुएं अपरिवर्तनीय हैं, तो सिंगलटन के पास कोई वैश्विक स्थिति नहीं है, क्योंकि सब कुछ स्थिर है। लेकिन इस तरह के सिंगलटन को उत्परिवर्तित में बदलना बहुत आसान है, यह बहुत फिसलन ढलान है। इसलिए, मैं इन सिंगलेटों के खिलाफ भी हूं, इसलिए नहीं कि वे बुरे हैं, बल्कि इसलिए कि उनके लिए बुरा जाना बहुत आसान है। (एक साइड नोट के रूप में जावा एन्यूमरेशन केवल इस तरह के सिंगललेट हैं। जब तक आप अपने एन्यूमरेशन में स्टेट नहीं डालते हैं आप ठीक हैं, इसलिए कृपया नहीं।)
दूसरी तरह के सिंगलेट्स, जो अर्ध-स्वीकार्य हैं, जो आपके कोड के निष्पादन को प्रभावित नहीं करते हैं, उनका कोई "साइड इफेक्ट" नहीं है। लॉगिंग सही उदाहरण है। यह सिंगलेट्स और वैश्विक राज्य से भरा हुआ है। यह स्वीकार्य है (जैसा कि इसमें आपको कोई चोट नहीं पहुंचेगी) क्योंकि आपका एप्लिकेशन कोई भिन्न व्यवहार नहीं करता है या नहीं एक दिया गया लकड़हारा सक्षम है। यहां जानकारी एक तरह से बहती है: आपके आवेदन से लकड़हारे में। यहां तक कि सोचा गया कि लॉगर वैश्विक स्थिति है क्योंकि लॉगर से कोई जानकारी आपके आवेदन में प्रवाहित नहीं होती है, लकड़हारे स्वीकार्य हैं। यदि आप चाहते हैं कि आपका परीक्षण कुछ लॉग हो रहा है, तो यह सुनिश्चित करने के लिए आपको अपने लकड़हारे को इंजेक्ट करना चाहिए, लेकिन सामान्य रूप से लकड़हारे राज्य से भरे होने के बावजूद हानिकारक नहीं होते हैं।
foo.x
या यदि आपFoo.x
इसके बजाय जोर देते हैंFoo().x
); वर्ग विशेषताओं और स्थिर / वर्ग विधियों (Foo.x
) का उपयोग करें।