2012-04-12 16 views
9

कहें कि मेरे पास कक्षा A, B और C है।एमआरओ त्रुटियों के बिना मैं गतिशील रूप से बेस क्लास के रूप में मिश्रित कैसे जोड़ूं?

कक्षा A और B कक्षा C के लिए दोनों मिश्रित कक्षाएं हैं।

class A(object): 
    pass 
class B(object): 
    pass 
class C(object, A, B): 
    pass 

यह जब वर्ग सी instantiating मैं इसे काम करने के लिए वर्ग सी से object को दूर करने के लिए होता है काम नहीं करेगा। (अन्यथा आपको एमआरओ समस्याएं मिलेंगी)।

TypeError: Error when calling the metaclass bases
Cannot create a consistent method resolution
order (MRO) for bases B, object, A

हालांकि, मेरा मामला थोड़ा और जटिल है। मेरे मामले वर्ग C में सर्वर है जहां A और B स्टार्टअप पर लोड होने वाले प्लगइन होंगे। ये अपने फ़ोल्डर में रह रहे हैं।

मेरे पास Cfactory नामक कक्षा भी है। Cfactory में मैं एक __new__ विधि है कि, एक पूरी तरह कार्यात्मक वस्तु सी __new__ विधि में मैं प्लग-इन के लिए खोज पैदा करेगा __import__ का उपयोग कर उन्हें लोड, और फिर उन्हें C.__bases__ += (loadedClassTypeGoesHere,)

को असाइन तो निम्नलिखित एक संभावना है है: (यह काफी सार)

class A(object): 
    def __init__(self): pass 
    def printA(self): print "A" 
class B(object): 
    def __init__(self): pass 
    def printB(self): print "B" 
class C(object): 
    def __init__(self): pass 
class Cfactory(object): 
    def __new__(cls): 
     C.__bases__ += (A,) 
     C.__bases__ += (B,) 
     return C() 

यह फिर से काम नहीं करेगा, और एमआरओ त्रुटियों फिर से दे देंगे बनाया:

TypeError: Cannot create a consistent method resolution
order (MRO) for bases object, A

एक इसके लिए आसान फिक्स बेसक्लास को A और B से हटा रहा है। हालांकि यह उन्हें पुरानी शैली जिन वस्तुओं पर जब इन प्लग इन चलाने जा रहा है स्टैंड-अलोन से बचा जाना चाहिए (जो संभव हो जाना चाहिए, unittest वार)

एक और आसान ठीक C से object को हटा रहा है कर देगा, लेकिन यह भी यह एक कर देगा पुरानी शैली की कक्षा और C.__bases__ अनुपलब्ध होगा इस प्रकार मैं C

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

+2

पुरानी शैली कक्षाएं पायथन 3 में मौजूद नहीं हैं, इसलिए आपको अंततः समाधान की आवश्यकता होगी। –

+0

वास्तव में वास्तव में सच है। –

उत्तर

10

इस तरह से सोचें - आप चाहते हैं कि मिश्रण object के कुछ व्यवहारों को ओवरराइड करें, इसलिए उन्हें विधि समाधान क्रम में object से पहले होने की आवश्यकता है।

तो तुम ठिकानों के आदेश को बदलने की जरूरत: और

class C(A, B, object): 
    pass 

कारण this bug के लिए, आप C जरूरत वस्तु से विरासत के लिए नहीं सीधे सही ढंग से __bases__ को आवंटित करने के लिए सक्षम होने के लिए, कारखाने वास्तव में सिर्फ सकता है एक समारोह हो:

class FakeBase(object): 
    pass 

class C(FakeBase): 
    pass 

def c_factory(): 
    for base in (A, B): 
     if base not in C.__bases__: 
      C.__bases__ = (base,) + C.__bases__ 
    return C() 
+1

हाँ मैंने आपके पहले उदाहरण की कोशिश की जो वास्तव में उस बग को दे रहा था (इसे आपके पहले से संपादित कोड के साथ भी मिला)। मुझे कुछ चीजें काम करने दें और देखें कि यह काम करता है या नहीं। –

+0

@DanTimmer हाँ, यह दुर्भाग्यपूर्ण है कि बग आधार के उचित क्रम के मुद्दे को भ्रमित करता है। – agf

3

मैं विवरण पता नहीं है, तो शायद मैं पूरी तरह से बंद आधार यहाँ हूँ, लेकिन यह है कि आप अपने डिजाइन को प्राप्त करने के गलत तंत्र का उपयोग कर रहे की तरह लगता है।

सबसे पहले, Cfactory एक कक्षा क्यों है, और इसकी __new__ विधि किसी और चीज का उदाहरण क्यों देती है? यह एक स्वाभाविक रूप से एक समारोह है जो लागू करने के लिए एक विचित्र तरीका की तरह दिखता है। Cfactory जैसा कि आपने इसे वर्णित किया है (और एक सरलीकृत उदाहरण दिखाया गया है) किसी वर्ग की तरह व्यवहार नहीं करता है; आपके पास कार्यक्षमता साझा करने वाले कई उदाहरण नहीं हैं (वास्तव में ऐसा लगता है कि आपने स्वाभाविक रूप से उदाहरणों को बनाना असंभव बना दिया है)।

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

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

तो प्लगइन्स समय C लोड नहीं किया जा सकता है बनाया गया है, तो मैं मैन्युअल बिंदु पर विन्यासक classmethod लागू जब तुम, प्लग इन के लिए खोज सकते से पहले C उदाहरण बनाई गई है साथ जाना होगा।

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

def Cfactory(*args, **kwargs): 
    plugins = find_plugins() 
    bases = (C,) + plugins 
    cls = type('C_with_plugins', bases, {}) 
    return cls(*args, **kwargs) 

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

+0

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

+1

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

3

एक साधारण कामकाज है: एक अच्छा नाम के साथ एक सहायक वर्ग बनाएं, जैसे प्लगइनबेस। और ऑब्जेक्ट के बजाए, इसके उत्तराधिकारी का उपयोग करें।

यह कोड को और अधिक पठनीय (imho) बनाता है और यह बग को परिस्थितियों में बनाता है।

class PluginBase(object): pass 
class ServerBase(object): pass 

class pluginA(PluginBase): "Now it is clearly a plugin class" 
class pluginB(PluginBase): "Another plugin" 

class Server1(ServerBase, pluginA, pluginB): "This works" 
class Server2(ServerBase): pass 
Server2.__base__ += (PluginA,) # This also works 

नोट के रूप में: शायद आप कारखाने की जरूरत नहीं है; सी ++ में इसकी आवश्यकता है, लेकिन शायद ही कभी पाइथन

+0

मैं ओएस एक्स पर पायथन 2.7.6 का उपयोग कर रहा हूं और आपकी आखिरी पंक्ति काम नहीं करती है। मुझे "TypeError: + =: 'type' और 'tuple' के लिए असमर्थित ऑपरेंड प्रकार (" पूंजी पी के टाइपो को ठीक करने के बाद) मिलता है। – mikeazo

संबंधित मुद्दे