मैं बेस क्लास से गतिशील रूप से व्युत्पन्न कक्षाएं कैसे बना सकता हूं


92

उदाहरण के लिए मेरे पास आधार वर्ग निम्नानुसार है:

class BaseClass(object):
    def __init__(self, classtype):
        self._type = classtype

इस वर्ग से मैं कई अन्य वर्गों को प्राप्त करता हूं, जैसे

class TestClass(BaseClass):
    def __init__(self):
        super(TestClass, self).__init__('Test')

class SpecialClass(BaseClass):
    def __init__(self):
        super(TestClass, self).__init__('Special')

क्या एक फ़ंक्शन कॉल द्वारा गतिशील रूप से उन कक्षाओं को बनाने का एक अच्छा, पायथोनिक तरीका है जो नई कक्षा को मेरे वर्तमान दायरे में रखता है, जैसे:

foo(BaseClass, "My")
a = MyClass()
...

जैसा कि टिप्पणी और प्रश्न होंगे कि मुझे इसकी आवश्यकता क्यों है: व्युत्पन्न वर्गों में अंतर के साथ सटीक एक ही आंतरिक संरचना होती है, कि निर्माणकर्ता पहले से अपरिभाषित तर्कों की संख्या लेता है। इसलिए, उदाहरण के लिए, क्लास के कंस्ट्रक्टर को लेने के दौरान MyClassकीवर्ड लेता है और ।aTestClassbc

inst1 = MyClass(a=4)
inst2 = MyClass(a=5)
inst3 = TestClass(b=False, c = "test")

लेकिन उन्हें इनपुट तर्क की तरह वर्ग के प्रकार का उपयोग नहीं करना चाहिए

inst1 = BaseClass(classtype = "My", a=4)

मुझे यह काम करने के लिए मिला, लेकिन दूसरे तरीके से, यानी गतिशील रूप से बनाई गई क्लास ऑब्जेक्ट पसंद करेंगे।


बस सुनिश्चित करने के लिए, आप आपूर्ति किए गए तर्कों के आधार पर किस प्रकार का उदाहरण बदलना चाहते हैं? जैसे अगर मैं देता हूं तो aयह हमेशा रहेगा MyClassऔर TestClassकभी नहीं लेगा a? क्यों नहीं सभी 3 तर्कों की घोषणा करते हैं, BaseClass.__init__()लेकिन उन सभी को डिफ़ॉल्ट रूप से None? def __init__(self, a=None, b=None, C=None)?
acattle

मैं बेस क्लास में कुछ भी घोषित नहीं कर सकता, क्योंकि मैं उन सभी तर्कों को नहीं जानता जो मैं उपयोग कर सकता हूं। मेरे पास 5 अलग-अलग तर्कों के साथ 30 अलग-अलग मामले हो सकते हैं, इसलिए कंस्ट्रक्टर में 150 तर्कों की घोषणा करना एक समाधान नहीं है।
एलेक्स

जवाबों:


141

यह बिट कोड आपको गतिशील नाम और पैरामीटर नाम के साथ नई कक्षाएं बनाने की अनुमति देता है। __init__बस में पैरामीटर सत्यापन अज्ञात मापदंडों की अनुमति नहीं देता है, यदि आपको अन्य सत्यापन की आवश्यकता है, जैसे प्रकार, या वे अनिवार्य हैं, तो बस तर्क जोड़ें:

class BaseClass(object):
    def __init__(self, classtype):
        self._type = classtype

def ClassFactory(name, argnames, BaseClass=BaseClass):
    def __init__(self, **kwargs):
        for key, value in kwargs.items():
            # here, the argnames variable is the one passed to the
            # ClassFactory call
            if key not in argnames:
                raise TypeError("Argument %s not valid for %s" 
                    % (key, self.__class__.__name__))
            setattr(self, key, value)
        BaseClass.__init__(self, name[:-len("Class")])
    newclass = type(name, (BaseClass,),{"__init__": __init__})
    return newclass

और यह इस तरह काम करता है, उदाहरण के लिए:

>>> SpecialClass = ClassFactory("SpecialClass", "a b c".split())
>>> s = SpecialClass(a=2)
>>> s.a
2
>>> s2 = SpecialClass(d=3)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 8, in __init__
TypeError: Argument d not valid for SpecialClass

मैं देखता हूं कि आप नामकरण के दायरे में गतिशील नाम डालने के लिए कह रहे हैं - अब, जिसे पायथन में एक अच्छा अभ्यास नहीं माना जाता है - आपके पास या तो चर नाम हैं, जिन्हें कोडिंग समय पर जाना जाता है, या डेटा - और रनटाइम में सीखे गए नाम अधिक हैं " "वैरिएबल" की तुलना में डेटा -

इसलिए, आप अपनी कक्षाओं को एक शब्दकोश में जोड़ सकते हैं और वहां से उनका उपयोग कर सकते हैं:

name = "SpecialClass"
classes = {}
classes[name] = ClassFactory(name, params)
instance = classes[name](...)

और अगर आपके डिज़ाइन को पूरी तरह से नामों की आवश्यकता है, तो बस गुंजाइश करें, लेकिन globals() एक मनमाने तरीके के बजाय कॉल द्वारा लौटाए गए शब्दकोश का उपयोग करें:

name = "SpecialClass"
globals()[name] = ClassFactory(name, params)
instance = SpecialClass(...)

(यह वास्तव में संभव है कि क्लास फैक्ट्री फ़ंक्शन के लिए कॉलर के वैश्विक दायरे पर नाम सम्मिलित किया जाए - लेकिन यह और भी बुरा अभ्यास है, और पायथन कार्यान्वयन के अनुकूल नहीं है। ऐसा करने का तरीका कॉलर को प्राप्त करना होगा। निष्पादन फ्रेम, sys._getframe (1) के माध्यम से और फ्रेम का वैश्विक शब्दकोश में इसकी f_globalsविशेषता में वर्ग नाम सेट करना )।

अद्यतन, टीएल; डॉ: यह उत्तर लोकप्रिय हो गया था, फिर भी यह प्रश्न निकाय के लिए बहुत विशिष्ट है। पायथन में "गतिशील रूप से व्युत्पन्न वर्गों को आधार वर्ग से कैसे बनाएं" पर सामान्य उत्तर typeनए वर्ग के नाम को पारित करने के लिए एक सरल कॉल है , बेसकलैस के साथ एक ट्यूपल (एस) और __dict__नए वर्ग के लिए शरीर इस तरह:

>>> new_class = type("NewClassName", (BaseClass,), {"new_method": lambda self: ...})

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


2
अगर मुझे सही तरीके से याद है, BaseClass.__init__()तो अधिक सामान्य के रूप में बेहतर होगा super(self.__class__).__init__(), जो नए वर्गों के उपवर्ग में अधिक अच्छी तरह से खेलता है। (संदर्भ: rhettinger.wordpress.com/2011/05/26/super-considered-super )
एरिक ओ लेबिगोट

@ ईओएल: यह सांख्यिकीय रूप से घोषित वर्गों के लिए होगा - लेकिन चूंकि आपके पास सुपर के पहले पैरामीटर के रूप में हार्डकोड करने के लिए वास्तविक वर्ग का नाम नहीं है, इसके लिए बहुत सारे नृत्य की आवश्यकता होगी। इसे superऊपर से बदलने की कोशिश करें और इसे समझने के लिए गतिशील रूप से बनाई गई कक्षा का एक उपवर्ग बनाएं; और, दूसरी ओर, इस मामले में आपके पास एक सामान्य ऑब्जेक्टफ्रेम के रूप में आधार रेखा हो सकती है जिसे कॉल करना है __init__
jsbueno

अब मेरे पास सुझाए गए समाधान को देखने के लिए कुछ समय था, लेकिन यह वह नहीं है जो मैं चाहता हूं। सबसे पहले, यह की तरह लग रहा __init__की BaseClassएक तर्क के साथ कहा जाता है, लेकिन वास्तव में BaseClass.__init__हमेशा कीवर्ड युक्तियों के स्वेच्छाचारी सूची लेता है। दूसरा, उपरोक्त समाधान सभी अनुमत पैरामीटर नामों को विशेषताओं के रूप में सेट करता है, जो कि मैं नहीं चाहता। किसी भी तर्क के लिए जाना है BaseClass, लेकिन जो मैं जानता हूँ कि व्युत्पन्न वर्ग बनाते समय। मैं शायद सवाल को अपडेट करूंगा या इसे स्पष्ट करने के लिए अधिक सटीक पूछूंगा।
एलेक्स

@jsbueno: ठीक है, super()मैं का उपयोग करता था उल्लेख करता है TypeError: must be type, not SubSubClass। अगर मैं सही ढंग से समझूं, तो यह पहली दलील से आता selfहै __init__(), जो एक ऐसी SubSubClassजगह है जहां एक typeवस्तु की उम्मीद की जाती है: यह इस तथ्य से संबंधित लगता है super(self.__class__)कि एक अनबाउंड सुपर ऑब्जेक्ट है। इसकी __init__()विधि क्या है ? मुझे यकीन नहीं है कि इस तरह की विधि के लिए पहले प्रकार के तर्क की आवश्यकता हो सकती है type। क्या आप समझाएँगे? (साइड नोट: मेरा super()दृष्टिकोण वास्तव में समझ में नहीं आता है, यहां, क्योंकि __init__()एक चर हस्ताक्षर है।)
एरिक ओ लेबिगॉट

1
@ ईओएल: वास्तव में प्रमुख समस्या यह है कि यदि आप कारक वर्ग का एक और उपवर्ग बनाते हैं: स्व .__ वर्ग__ उस उपवर्ग को संदर्भित करेगा, न कि उस वर्ग को जिसमें "सुपर" कहा जाता है - और आपको अनंत पुनरावृत्ति मिलती है।
jsbueno

89

type() वह कार्य है जो कक्षाएं बनाता है (और विशेष रूप से उप-वर्गों में):

def set_x(self, value):
    self.x = value

SubClass = type('SubClass', (BaseClass,), {'set_x': set_x})
# (More methods can be put in SubClass, including __init__().)

obj = SubClass()
obj.set_x(42)
print obj.x  # Prints 42
print isinstance(obj, BaseClass)  # True

पायथन 2.7 का उपयोग करके इस उदाहरण को समझने की कोशिश में, मुझे TypeErrorऐसा कहा गया __init__() takes exactly 2 arguments (1 given)। मैंने पाया कि अंतर को भरने के लिए कुछ (कुछ भी?) जोड़ना पर्याप्त होगा। उदाहरण के लिए, obj = SubClass('foo')त्रुटि के बिना चलता है।
डेव्ल 1717

यह सामान्य है, क्योंकि प्रश्न में SubClassएक उप-वर्ग है BaseClassऔर BaseClassएक पैरामीटर लेता है ( classtype, जो 'foo'आपके उदाहरण में है)।
बजे एरिक ओ लेबिगोट

-2

एक गतिशील विशेषता मान के साथ एक वर्ग बनाने के लिए, नीचे दिए गए कोड की जांच करें। एनबी। यह अजगर प्रोग्रामिंग भाषा में कोड स्निपेट्स हैं

def create_class(attribute_data, **more_data): # define a function with required attributes
    class ClassCreated(optional extensions): # define class with optional inheritance
          attribute1 = adattribute_data # set class attributes with function parameter
          attribute2 = more_data.get("attribute2")

    return ClassCreated # return the created class

# use class

myclass1 = create_class("hello") # *generates a class*
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.