मैंने इस प्रश्न के उत्तर के लिए चारों ओर खोज की है लेकिन कुछ भी नहीं मिला। मेरी माफ़ी अगर यह पहले ही पूछा जा चुका है।

माता-पिता वर्ग से बाल वर्ग पर दी गई विधि को लागू करने के लिए मुझे पता है कि 3-4 विधियों में से (__new__ मेटाक्लास की विधि को संपादित करना, builtins.__build_class__ में हुक करना, __init_subclass__ का उपयोग करना या abc.abstractmethod का उपयोग करके) मैं आमतौर पर __init_subclass__ का उपयोग करता हूं, मूल रूप से उपयोग में आसानी के कारण और, @abc.abstractmethod के विपरीत, चाइल्ड क्लास पर बाधा को चाइल्ड क्लास डेफिनिशन पर चेक किया जाता है न कि क्लास इंस्टेंटेशन पर . उदाहरण:

class Par():
    def __init_subclass__(self, *args, **kwargs):
        must_have = 'foo'
        if must_have not in list(self.__dict__.keys()):
            raise AttributeError(f"Must have {must_have}")

    def __init__(self):
        pass

class Chi(Par):
    def __init__(self):
        super().__init__()

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

def add_hello_world(Cls):
    class NewCls(object):
        def __init__(self, *args, **kwargs):
            self.instance = Cls(*args, **kwargs)

        def hello_world(self):
            print("hello world")

    return NewCls


@add_hello_world
class Par:
    def __init_subclass__(self, *args, **kwargs):
        must_have = "foo"
        if must_have not in list(self.__dict__.keys()):
            raise AttributeError(f"Must have {must_have}")

    def __init__(self):
        pass

class Chi(Par):
    def __init__(self):
        super().__init__()

c = Chi()
c.hello_world()

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

  • क्या यह एक इच्छित पायथन व्यवहार है? या यह किसी प्रकार की बग है? ईमानदार होने के लिए, मेरी समझ में, यह कुछ सुरक्षा चिंताओं को प्रस्तुत कर सकता है।

  • क्या यह केवल __init_subclass__ डेटा मॉडल के साथ होता है? या दूसरों को भी?

2
mosegui 5 सितंबर 2019, 22:22

1 उत्तर

सबसे बढ़िया उत्तर

याद रखें, डेकोरेटर सिंटैक्स सिर्फ फंक्शन एप्लीकेशन है:

class Par:
    def __init_subclass__(...):
         ...


Par = add_hello_world(Par)

वर्ग मूल रूप से Par परिभाषित __init_subclass__ के लिए बाध्य है; add_hello_world के अंदर परिभाषित नया वर्ग नहीं है, और यह वह वर्ग है जिसे पोस्ट-डेकोरेशन नाम Par संदर्भित करता है, और वह वर्ग जिसे आप उपवर्ग कर रहे हैं।


संयोग से, आप अभी भी Par के माध्यम से __init__ के मूल वर्ग तक पहुंच सकते हैं।

डेकोरेटर को स्पष्ट रूप से कॉल करना:

class Par:
    def __init_subclass__(self, *args, **kwargs):
        must_have = "foo"
        if must_have not in list(self.__dict__.keys()):
            raise AttributeError(f"Must have {must_have}")

    def __init__(self):
        pass

Foo = Par  # Keep this for confirmation

Par = add_hello_world(Par)

हम पुष्टि कर सकते हैं कि क्लोजर मूल वर्ग का संदर्भ रखता है:

>>> Par.__init__.__closure__[0].cell_contents
<class '__main__.Par'>
>>> Par.__init__.__closure__[0].cell_contents is Par
False
>>> Par.__init__.__closure__[0].cell_contents is Foo
True

और यदि आपने किया इसे उपवर्ग करने का प्रयास किया है, तो आपको अपेक्षित त्रुटि मिलेगी:

>>> class Bar(Par.__init__.__closure__[0].cell_contents):
...   pass
...
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "tmp.py", line 16, in __init_subclass__
    raise AttributeError(f"Must have {must_have}")
AttributeError: Must have foo
6
chepner 5 सितंबर 2019, 19:43