2012-06-18 25 views
5

का उपयोग कर this answer के बाद ऐसा लगता है कि एक वर्ग metaclass' metaclass के बाद वर्ग परिभाषित किया गया है बदला जा सकता है निम्नलिखित * का उपयोग करके कक्षा की स्थापना:'एक डेकोरेटर

class MyMetaClass(type): 
    # Metaclass magic... 

class A(object): 
    pass 

A = MyMetaClass(A.__name__, A.__bases__, dict(A.__dict__)) 

एक समारोह

def metaclass_wrapper(cls): 
    return MyMetaClass(cls.__name__, cls.__bases__, dict(cls.__dict__)) 
को परिभाषित करना

मुझे इतना की तरह एक वर्ग परिभाषा करने के लिए एक डेकोरेटर लागू करने के लिए अनुमति देता है,

@metaclass_wrapper 
class B(object): 
    pass 

यह देखना एमएस कि मेटाक्लास जादू B पर लागू होता है, हालांकि B में __metaclass__ विशेषता नहीं है। उपरोक्त विधि वर्ग परिभाषाओं को metaclasses लागू करने के लिए एक समझदार तरीका है, भले ही मैं definiting कर रहा हूँ और एक वर्ग फिर से definiting, या मैं बेहतर बस

class B(object): 
    __metaclass__ = MyMetaClass 
    pass 

मुझे लगता है इन दोनों के बीच कुछ मतभेद हैं लिख होगा तरीकों।


* नोट, जुड़ा हुआ सवाल है, MyMetaClass(A.__name__, A.__bases__, A.__dict__) में मूल जवाब, वापस आने वाले TypeError:

TypeError: type() argument 3 must be a dict, not dict_proxy

ऐसा लगता है कि A (वर्ग परिभाषा) की __dict__ विशेषता, एक प्रकार dict_proxy है, जबकि __dict__उदाहरणA के प्रकार dict का प्रकार है। ऐसा क्यों है? क्या यह एक पायथन 2.x बनाम 3.x अंतर है?

उत्तर

1

कक्षा में __metaclass__ विशेषता सेट नहीं है ... क्योंकि आपने इसे कभी सेट नहीं किया है!

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

वास्तव में, सामान्य वर्गों metaclass type के सभी उदाहरणों हैं, इसलिए यदि metaclass हमेशा तो इसके उदाहरणों पर __metaclass__ विशेषता निर्धारित हर वर्ग एक __metaclass__ विशेषता (उनमें से ज्यादातर type के लिए सेट) होगा।


मैं आपके सजावटी दृष्टिकोण का उपयोग नहीं करता। यह इस तथ्य को अस्पष्ट करता है कि एक मेटाक्लास शामिल है (और कौन सा), अभी भी बॉयलरप्लेट की एक पंक्ति है, और (name, bases, attributes) की 3 परिभाषित विशेषताओं से कक्षा बनाने के लिए केवल गड़बड़ है, परिणामस्वरूप कक्षा से उन 3 बिटों को वापस खींचने के लिए, कक्षा को फेंक दो, और उन 3 बिट्स से एक नई कक्षा बनाएं!

जब आप पायथन 2.x में ऐसा करते हैं:

class A(object): 
    __metaclass__ = MyMeta 
    def __init__(self): 
     pass 

आप लगभग एक ही परिणाम मिलता है अगर तुम लिखा था कि हम इस:

attrs = {} 
attrs['__metaclass__'] = MyMeta 
def __init__(self): 
    pass 
attrs['__init__'] = __init__ 
A = attrs.get('__metaclass__', type)('A', (object,), attrs) 

हकीकत में metaclass की गणना अधिक है जटिल है, क्योंकि वास्तव में यह निर्धारित करने के लिए सभी आधारों के माध्यम से एक खोज होनी चाहिए कि मेटाक्लास संघर्ष है या नहीं, और यदि किसी बेस के पास type नहीं है, तो इसके मेटाक्लास और attrs में __metaclass__ नहीं है तो डिफ़ॉल्ट मेटाक्लास type के बजाय पूर्वजों का मेटाक्लास। यह एक ऐसी स्थिति है जहां मुझे उम्मीद है कि आपका सजावट "समाधान" __metaclass__ का उपयोग करने से अलग होगा। मुझे यकीन नहीं है कि क्या होगा यदि आपने अपने सजावट का इस्तेमाल ऐसी स्थिति में किया जहां __metaclass__ का उपयोग करके आपको मेटाक्लास संघर्ष त्रुटि मिलती है, लेकिन मैं इसे सुखद होने की उम्मीद नहीं करता।

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

__dict__ के लिए आपको एक वास्तविक शब्दकोश नहीं दे रहा है, यह केवल एक कार्यान्वयन विस्तार है; मैं प्रदर्शन कारणों से अनुमान लगाऊंगा। मुझे संदेह है कि कोई भी स्पेस है जो कहता है कि __dict__ (गैर-वर्ग) उदाहरण का एक वर्ग के __dict__ के समान प्रकार होना चाहिए (जो एक उदाहरण बीटीडब्ल्यू है, केवल मेटाक्लास का एक उदाहरण है)। __dict__ कक्षा का गुण "dictproxy" है, जो आपको विशेषता कुंजी देखने की अनुमति देता है जैसे कि यह dict था लेकिन अभी भी dict नहीं है। type अपने तीसरे तर्क के प्रकार के बारे में picky है; यह सिर्फ एक "dict-like" ऑब्जेक्ट (डक-टाइपिंग खराब करने के लिए शर्मिंदा) नहीं, बल्कि एक असली निर्देश चाहता है। यह 2.x बनाम 3.x चीज नहीं है; पायथन 3 वैसे ही व्यवहार करता है, हालांकि यह आपको dictproxy का एक अच्छा स्ट्रिंग प्रतिनिधित्व देता है। पायथन 2.4 (जो सबसे पुराना 2.x है, मैं आसानी से उपलब्ध हूं) dictproxy ऑब्जेक्ट्स __dict__ ऑब्जेक्ट्स के लिए ऑब्जेक्ट्स भी है।

1

आपके प्रश्न का मेरा सारांश: "मैंने कुछ करने का एक नया मुश्किल तरीका करने की कोशिश की, और यह काफी काम नहीं किया। क्या मुझे इसके बजाय सरल तरीके का उपयोग करना चाहिए?"

हां, आपको इसे सरल तरीका करना चाहिए। आपने यह नहीं कहा है कि आप ऐसा करने के लिए एक नया तरीका खोजने में रुचि क्यों रखते हैं।

+0

मैं बस सोच रहा था कि कक्षा सजावट में '__metaclass__' विशेषता को सेट करने के बराबर एक सजावटी था। मुझे लगता है कि मेरे प्रश्न का एक और हिस्सा यह है कि मेरे 'सजावटी' ने काम करने के लिए * प्रतीत किया था, लेकिन मैंने देखा कि सजाए गए वर्ग में '__metaclass__' विशेषता नहीं है, इसलिए स्पष्ट रूप से दो तरीकों के बीच एक अंतर है; यह क्यों है? – Chris

3

मान्य है, मैं पार्टी के लिए थोड़ा देर हो चुकी हूं। हालांकि, मैं गिर गया यह जोड़ने लायक था।

यह पूरी तरह से करने योग्य है। ऐसा कहा जा रहा है कि, एक ही लक्ष्य को पूरा करने के कई अन्य तरीके हैं। हालांकि, सजावट समाधान, विशेष रूप से, देरी मूल्यांकन (obj = dec(obj)) की अनुमति देता है, जो कक्षा के अंदर __metaclass__ का उपयोग नहीं करता है। ठेठ सजावटी शैली में, मेरा समाधान नीचे है।

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

व्यक्तिगत रूप से, मुझे यह ट्रैक रखने में सक्षम होना पसंद है कि ऑब्जेक्ट को कैसे लपेटा गया था। इसलिए, मैंने __wrapped__ विशेषता जोड़ा, जो कड़ाई से जरूरी नहीं है। यह कक्षाओं के लिए पायथन 3 में functools.wraps की तरह बनाता है। हालांकि, यह आत्मनिरीक्षण के साथ सहायक हो सकता है। इसके अलावा, __metaclass__ सामान्य मेटाक्लास उपयोग केस की तरह कार्य करने के लिए जोड़ा जाता है।

def metaclass(meta): 
    def metaclass_wrapper(cls): 
     __name = str(cls.__name__) 
     __bases = tuple(cls.__bases__) 
     __dict = dict(cls.__dict__) 

     for each_slot in __dict.get("__slots__", tuple()): 
      __dict.pop(each_slot, None) 

     __dict["__metaclass__"] = meta 

     __dict["__wrapped__"] = cls 

     return(meta(__name, __bases, __dict)) 
    return(metaclass_wrapper) 

एक मामूली उदाहरण के लिए, निम्नलिखित लें।

class MetaStaticVariablePassed(type): 
    def __new__(meta, name, bases, dct): 
     dct["passed"] = True 

     return(super(MetaStaticVariablePassed, meta).__new__(meta, name, bases, dct)) 

@metaclass(MetaStaticVariablePassed) 
class Test(object): 
    pass 

यह अच्छा परिणाम पैदावार ...

|1> Test.passed 
|.> True 

कम सामान्य में डेकोरेटर, लेकिन समान रास्ते का उपयोग करना ...

class Test(object): 
    pass 

Test = metaclass_wrapper(Test) 

... पैदावार, के रूप में उम्मीद , वही अच्छा परिणाम।

|1> Test.passed 
|.> True 
संबंधित मुद्दे