2013-08-08 3 views
60

क्या मेरी कक्षा उप-वर्गीकृत होने पर कोड ट्रिगर करने का कोई तरीका है?पायथन: जब कक्षा को उप-वर्गीकृत किया जाता है तो कोड चलाएं

class SuperClass: 
    def triggered_routine(subclass): 
     print("was subclassed by " + subclass.__name__) 

magically_register_triggered_routine() 

print("foo") 

class SubClass0(SuperClass): 
    pass 

print("bar") 

class SubClass1(SuperClass): 
    print("test") 

चाहिए उत्पादन

foo 
was subclassed by SubClass0 
bar 
test 
was subclassed by SubClass1 
+1

मेटाक्लास का उपयोग करें; वर्गों को बनाए जाने पर मेटाक्लास को बुलाया जाता है, जैसे कि उदाहरण बनाए जाने पर वर्गों को बुलाया जाता है। –

+2

कोई उत्तर नहीं जोड़ सकता है, लेकिन आज python3.6 में '__init_subclass__' है - इसे जांचें! –

+1

@OrDuan: धन्यवाद, उपयोगी लगता है। इस सवाल को एक डुप्लिकेट को अन-मार्क करने के लिए भी पर्याप्त कारण हो सकता है, क्योंकि अब "मेटाक्लास का उपयोग करें" की बजाय मेरी समस्या के लिए एक समर्पित समाधान है। –

उत्तर

39

वर्ग (डिफ़ॉल्ट रूप से) type के उदाहरण हैं। बस Foo के उदाहरण के रूप में foo = Foo(...), type (यानी एक वर्ग) का उदाहरण myclass = type(name, bases, clsdict) द्वारा बनाया गया है।

यदि आप वर्ग निर्माण के पल में कुछ खास होना चाहते हैं, तो आपको कक्षा बनाने वाली चीज़ को संशोधित करना होगा - यानी type। ऐसा करने का तरीका type के उप-वर्ग को परिभाषित करना है - यानी मेटाक्लास। के रूप में एक वर्ग अपनी उदाहरण है

एक metaclass अपने वर्ग के लिए है।

को Python2 में आप

class SuperClass: 
    __metaclass__ = Watcher 

जहां Watchertype का एक उपवर्ग है के साथ एक वर्ग के metaclass को परिभाषित करेगा।

python3 में वाक्य रचना

class SuperClass(metaclass=Watcher) 

को बदल दिया गया है दोनों

Superclass = Watcher(name, bases, clsdict) 

जहां इस मामले में, name स्ट्रिंग 'Superclass' के बराबर होती है के बराबर हैं, और bases टपल (object,) है। clsdict कक्षा परिभाषा के शरीर में परिभाषित वर्ग विशेषताओं का एक शब्दकोश है।

नोट myclass = type(name, bases, clsdict) को समानता।

तो, बस के रूप में आप एक वर्ग के __init__ एक उदाहरण के निर्माण के समय की घटनाओं को नियंत्रित करने के प्रयोग करेंगे, तो आप एक वर्ग के निर्माण के समय की घटनाओं के साथ नियंत्रित कर सकते हैं एक metaclass के __init__:


class Watcher(type): 
    def __init__(cls, name, bases, clsdict): 
     if len(cls.mro()) > 2: 
      print("was subclassed by " + name) 
     super(Watcher, cls).__init__(name, bases, clsdict) 

class SuperClass: 
    __metaclass__ = Watcher 


print("foo") 

class SubClass0(SuperClass): 
    pass 

print("bar") 

class SubClass1(SuperClass): 
    print("test") 

प्रिंट

foo 
was subclassed by SubClass0 
bar 
test 
was subclassed by SubClass1 
+2

धन्यवाद। हालांकि, आपका कोड केवल python2 में काम करता है। पायथन 3 में, सुपर क्लास को 'क्लास सुपर क्लास (मेटाक्लास = वॉचर) होना चाहिए: पास –

+0

धन्यवाद। मैंने पायथन 2 और पायथन 3 सिंटैक्स में अंतर को संबोधित करने के लिए पोस्ट संपादित किया है। – unutbu

+1

जब मैंने अपनी कोशिश की तो मैंने दो महत्वपूर्ण चीजें याद की: 1 - 'वॉचर' सबक्लास 'टाइप',' ऑब्जेक्ट 'नहीं। 2 - 'सुपर क्लास' में 'ऑब्जेक्ट' नहीं, बिल्कुल कोई सुपरक्लास नहीं है। मैं बस 'ऑब्जेक्ट' को प्रत्येक वर्ग के लिए सुपरक्लास के रूप में लिखने के लिए उपयोग किया जाता हूं जिसे मैंने परिभाषित किया है कि मैंने किसी भी तरह इसे याद किया है कि यह कोड कुछ और उपयोग कर रहा था। यद्यपि 'ऑब्जेक्ट' से 'सुपर क्लास' प्राप्त करने में कोई हानि नहीं लगती है ... 'वॉचर 'के लिए' ऑब्जेक्ट 'नहीं,' ऑब्जेक्ट 'से प्राप्त करने के लिए निश्चित रूप से आवश्यक है। – ArtOfWarfare

6

संपादित करें: मेरी पुरानी पोस्ट वास्तव में काम नहीं करती है। classmethod से उपclassing अपेक्षित के रूप में काम नहीं करता है।

सबसे पहले, हम मेटाक्लास को यह बताने का कोई तरीका चाहते हैं कि इस विशेष विधि को उप-वर्ग व्यवहार पर विशेष कहा जाता है, हम केवल उस फ़ंक्शन पर एक विशेषता सेट करेंगे जिसे हम कॉल करना चाहते हैं। एक सुविधा के रूप में, हम फ़ंक्शन को classmethod में भी बदल देंगे ताकि वास्तविक बेसक्लस में पाया जा सके। हम क्लासमेड वापस कर देंगे ताकि इसे सजावटी के रूप में इस्तेमाल किया जा सके, जो सबसे सुविधाजनक है।

import types 
import inspect 

def subclass_hook(func): 
    func.is_subclass_hook = True 
    return classmethod(func) 

हम यह भी देखना होगा कि subclass_hook डेकोरेटर इस्तेमाल किया गया था एक सुविधाजनक तरीका चाहते हैं जा रहे हैं। हम जानते हैं कि classmethod का उपयोग किया गया है, इसलिए हम इसकी जांच करेंगे, और केवल तभी is_subclass_hook विशेषता देखें।

def test_subclass_hook(thing): 
    x = (isinstance(thing, types.MethodType) and 
     getattr(thing.im_func, 'is_subclass_hook', False)) 
    return x 

अंत में, हम एक metaclass कि सूचना के आधार पर काम करता है की जरूरत है: ज्यादातर मामलों के लिए, यहाँ करने के लिए सबसे दिलचस्प बात सिर्फ हुक के लिए आपूर्ति की ठिकानों में से प्रत्येक की जांच कर रहा है। इस तरह, सुपर कम से कम आश्चर्यजनक तरीके से काम करता है।

class MyMetaclass(type): 
    def __init__(cls, name, bases, attrs): 
     super(MyMetaclass, cls).__init__(name, bases, attrs) 

     for base in bases: 
      if base is object: 
       continue 
      for name, hook in inspect.getmembers(base, test_subclass_hook): 
       hook(cls) 

और इसे करना चाहिए।

>>> class SuperClass: 
...  __metaclass__ = MyMetaclass 
...  @subclass_hook 
...  def triggered_routine(cls, subclass): 
...   print(cls.__name__ + " was subclassed by " + subclass.__name__) 

>>> class SubClass0(SuperClass): 
...  pass 
SuperClass was subclassed by SubClass0 

>>> class SubClass1(SuperClass): 
...  print("test") 
test 
SuperClass was subclassed by SubClass1 
संबंधित मुद्दे