2012-01-15 15 views
6

मेरे पास मेटाक्लास के लिए एक अजीब और असामान्य उपयोग केस है जहां मैं परिभाषित किए जाने के बाद बेस क्लास के __metaclass__ को बदलना चाहता हूं ताकि इसके सबक्लास स्वचालित रूप से नए __metaclass__ का उपयोग कर सकें। लेकिन उस अजीब तरह से काम नहीं करता है:मैं कक्षा के __metaclass__ विशेषता को क्यों नहीं बदल सकता?

class MetaBase(type): 
    def __new__(cls, name, bases, attrs): 
     attrs["y"] = attrs["x"] + 1 
     return type.__new__(cls, name, bases, attrs) 

class Foo(object): 
    __metaclass__ = MetaBase 
    x = 5 

print (Foo.x, Foo.y) # prints (5, 6) as expected 

class MetaSub(MetaBase): 
    def __new__(cls, name, bases, attrs): 
     attrs["x"] = 11 
     return MetaBase.__new__(cls, name, bases, attrs) 

Foo.__metaclass__ = MetaSub 

class Bar(Foo): 
    pass 

print(Bar.x, Bar.y) # prints (5, 6) instead of (11, 12) 

क्या मैं बहुत अच्छी तरह से मूर्ख/असमर्थित/अपरिभाषित हो सकता है कर रहा हूँ, लेकिन मैं के लिए मुझे के जीवन को समझ नहीं सकता कितनी पुरानी metaclass लागू की जा रही है, और मैं कम से कम समझना चाहता हूं कि यह कैसे संभव है।

संपादित करें:jsbueno द्वारा किए गए एक सुझाव के आधार पर, मैं निम्न पंक्ति के साथ लाइन Foo.__metaclass__ = MetaSub है, जो किया मैं वास्तव में क्या चाहते थे की जगह:

Foo = type.__new__(MetaSub, "Foo", Foo.__bases__, dict(Foo.__dict__)) 

उत्तर

2

किसी वर्ग के लिए मेटाक्लास जानकारी का निर्माण इस समय किया जाता है जब इसे बनाया जाता है (या तो वर्ग ब्लॉक के रूप में पार्स किया जाता है, या गतिशील रूप से, मेटाक्लास के लिए स्पष्ट कॉल के साथ)। इसे बदला नहीं जा सकता है क्योंकि मेटाक्लास आमतौर पर कक्षा निर्माण समय में परिवर्तन करता है - निर्मित वर्ग प्रकार मेटाक्लास होता है। इसकी __metaclass__ विशेषता बनने के बाद यह अप्रासंगिक है।

हालांकि, किसी दिए गए वर्ग की एक प्रति बनाना संभव है, और प्रतिलिपि मूल वर्ग की तुलना में एक अलग मेटाक्लास है।

अपने उदाहरण पर, ऐसा करने का है, तो बजाय:

Foo.__metaclass__ = MetaSub

आप करते हैं:

Foo = Metasub("Foo", Foo.__bases__, dict(Foo.__dict__))

आप प्राप्त करेंगे कि तुम क्या चाहते थे। नया Foo सभी प्रभावों के लिए अपने पूर्ववर्ती के बराबर है, लेकिन एक अलग मेटाक्लास के साथ है।

हालांकि, Foo की पहले से मौजूद उदाहरणों नई Foo का एक उदाहरण विचार नहीं किया जाएगा - अगर आप, आप बेहतर बजाय एक अलग नाम के साथ Foo प्रतिलिपि बनाने की जरूरत है।

+0

टिप के लिए धन्यवाद, जो मैंने वही नहीं किया था, क्योंकि मैं अभी भी 'फू' के उप-वर्गों को मेटाक्लास रखना चाहता हूं, लेकिन मैंने अपने प्रश्न को उस समाधान को शामिल करने के लिए संपादित किया है जो आपके लिए आवश्यक था सुझाव। –

3

समस्या __metaclass__ विशेषता नहीं किया जाता है तब होता है जब विरासत में, आप जो उम्मीद कर सकते हैं उसके विपरीत। 'पुराना' मेटाक्लास को या तो Bar के लिए नहीं कहा जाता है।

उचित metaclass निम्नलिखित पूर्वता नियमों से निर्धारित होता है:

  • तो dict [ '__ metaclass__'] मौजूद है, यह प्रयोग किया जाता है डॉक्स कैसे metaclass पाया जाता है के बारे में निम्नलिखित का कहना है।
  • अन्यथा, यदि कम से कम एक बेस क्लास है, तो इसका मेटाक्लास उपयोग किया जाता है (यह पहले __class__ विशेषता के लिए दिखता है और यदि नहीं मिला, इसके प्रकार का उपयोग करता है)।
  • अन्यथा, यदि __metaclass__ नामक वैश्विक चर मौजूद है, तो इसका उपयोग किया जाता है।
  • अन्यथा, पुरानी शैली, क्लासिक मेटाक्लास (प्रकार। क्लासटाइप) का उपयोग किया जाता है।

तो क्या वास्तव में अपने Bar कक्षा में metaclass के रूप में प्रयोग किया जाता है माता पिता की __class__ विशेषता में और माता पिता की __metaclass__ विशेषता में नहीं मिला है।

अधिक जानकारी this StackOverflow answer पर मिल सकती है।

1

सबक्लास अपने माता-पिता के __metaclass__ का उपयोग करते हैं।

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

+0

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

+0

@EliCourtwright शायद आप अपनी खुद की कक्षा को रोल कर सकते हैं जो इसे प्राप्त करने के बजाय तीसरे पक्ष के मॉड्यूल में प्रतिनिधि करता है। कोड पुन: उपयोग को अधिकतम करते समय आपको कक्षा निर्माण प्रक्रिया पर पूर्ण नियंत्रण देना चाहिए। –

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