আমি কীভাবে বেস ক্লাস থেকে ডায়রিটিভ ক্লাস তৈরি করতে পারি


95

উদাহরণস্বরূপ আমার নীচে একটি বেস ক্লাস রয়েছে:

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কিওয়ার্ড লাগে aযখন ক্লাসের কন্সট্রাকটর TestClassলাগে bএবং c

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)?
অ্যাকিটল

বেস ক্লাসে আমি কোনও কিছুই ঘোষণা করতে পারি না, কারণ আমি যে সমস্ত যুক্তি ব্যবহার করতে পারি তা আমি জানি না। আমার প্রতিটি 5 টি পৃথক যুক্তি সহ 30 টি আলাদা ক্ল্যাস থাকতে পারে, তাই কনস্ট্রাক্টরে 150 টি আর্গুমেন্ট ঘোষণা করা কোনও সমাধান নয়।
অ্যালেক্স

উত্তর:


145

এই বিট কোডটি আপনাকে গতিশীল নাম এবং পরামিতি নাম সহ নতুন ক্লাস তৈরি করতে দেয়। __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(...)

(ক্লাস ফ্যাক্টরির কার্যক্রমে কলারের বিশ্বব্যাপী নামটি সন্নিবেশ করা সম্ভব ছিল - তবে এটি আরও খারাপ অনুশীলন, এবং পাইথন বাস্তবায়ন জুড়ে এটি উপযুক্ত নয় that এটি করার উপায়টি কলারের পেতে হবে এক্সিকিউশন ফ্রেম, sys._getframe (1) এর মাধ্যমে এবং ফ্রেমের গ্লোবাল অভিধানে শ্রেণীর নামটি তার f_globalsবৈশিষ্ট্যে সেট করে)।

আপডেট, টিএল; ডাঃ: এই উত্তরটি জনপ্রিয় হয়ে উঠেছে, এখনও এটি প্রশ্নবোধের সাথে খুব নির্দিষ্ট। পাইথনে "বেস ক্লাস থেকে উদ্ভূত শ্রেণিগুলি কীভাবে গতিশীলভাবে তৈরি করা যায় " সম্পর্কে সাধারণ উত্তরটি typeনতুন শ্রেণীর নাম, বেসক্লাস (এস) সহ একটি টুপল এবং __dict__নতুন বর্গের মতো শরীরকে পাস করার জন্য একটি সহজ কল call এটির মত:

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

আপডেট
যার যার প্রয়োজন এটি ডিল প্রকল্পটিও পরীক্ষা করে দেখা উচিত - এটি দাবি করে যে আচার সাধারণ বস্তুগুলির মতোই আচার এবং আনপিক ক্লাস করতে সক্ষম হবে এবং আমার কয়েকটি পরীক্ষায় এটি বেঁচে ছিল।


4
যদি আমি সঠিকভাবে মনে রাখি BaseClass.__init__()তবে আরও সাধারণ হিসাবে ভাল হবে super(self.__class__).__init__(), যা নতুন ক্লাসগুলি সাবক্ল্যাসড করার সময় আরও সুন্দরভাবে অভিনয় করে। (তথ্যসূত্র: rhettinger.wordpress.com/2011/05/26/super-cideide-super )
এরিক হে লেবিগোট

@ ইওল: এটি স্ট্যাটিকালি ঘোষিত ক্লাসগুলির জন্য হবে - তবে যেহেতু আপনার কাছে সুপারের প্রথম প্যারামিটার হিসাবে হার্ডকোডের প্রকৃত শ্রেণীর নাম নেই, এজন্য চারদিকে প্রচুর নাচ প্রয়োজন। এটি superউপরের সাথে প্রতিস্থাপনের চেষ্টা করুন এবং এটি বোঝার জন্য গতিশীলভাবে তৈরি শ্রেণীর একটি সাবক্লাস তৈরি করুন; এবং অন্যদিকে, এক্ষেত্রে আপনার বেসক্লাসটি একটি সাধারণ অবজেক্ট হিসাবে কল করতে হবে যা কল করতে হবে __init__
jsbueno

প্রস্তাবিত সমাধানটি দেখার জন্য এখন আমার কিছুটা সময় ছিল, তবে এটি আমি যা চাই তা ঠিক নয়। প্রথমত, এটা দেখে মনে হচ্ছে __init__এর BaseClassএকটি আর্গুমেন্ট সঙ্গে বলা হয়, কিন্তু আসলে BaseClass.__init__সবসময় শব্দ আর্গুমেন্ট একটি অবাধ তালিকা লাগে। দ্বিতীয়ত, উপরের সমাধানটি সমস্ত অনুমোদিত প্যারামিটারের নামগুলি অ্যাট্রিবিউট হিসাবে সেট করে, যা আমি চাই না। যে কোনও যুক্তিতে যেতে হবে BaseClass, তবে ডেরাইভেড ক্লাস তৈরি করার সময় কোনটি আমি জানি। আমি সম্ভবত প্রশ্নটি আপডেট করব বা আরও পরিষ্কার করে এটি পরিষ্কার করার জন্য বলব।
অ্যালেক্স

@ জসবুউনো: ঠিক আছে, super()আমি যেটি উল্লেখ করেছিলাম তা ব্যবহার করে TypeError: must be type, not SubSubClass। যদি আমি সঠিকভাবে বুঝতে, এই প্রথম যুক্তি থেকে আসে selfএর __init__(), যা একটি হল SubSubClassযেখানে একটি typeবস্তুর বলে আশা করা হচ্ছে: এই সত্য এর সাথে সম্পর্কিত বলে মনে হয় super(self.__class__)একটি আনবাউন্ড সুপার অবজেক্ট। এর __init__()পদ্ধতি কী ? আমি নিশ্চিত নই যে এই জাতীয় পদ্ধতিতে প্রথম ধরণের আর্গুমেন্টের প্রয়োজন হতে পারে type। আপনি ব্যাখ্যা করতে পারেন? (পার্শ্ব দ্রষ্টব্য: আমার super()দৃষ্টিভঙ্গিটি আসলে এখানে __init__()
কোনও অর্থবোধ

4
@ ইওল: প্রধান সমস্যাটি হ'ল প্রকৃতপক্ষে যদি আপনি গুণনীয়ক শ্রেণীর আরেকটি উপক্লাস তৈরি করেন: স্ব।
jsbueno

91

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')ত্রুটি ছাড়াই চলে।
ডেভএল

এটি স্বাভাবিক, যেহেতু প্রশ্নের SubClassএকটি উপ-শ্রেণি BaseClassএবং BaseClassএকটি পরামিতি নেয় ( classtypeযা 'foo'আপনার উদাহরণে রয়েছে)।
এরিক হে লেবিগোট

@ এরিকোলিবিগোট আমি কি এখানে কোনওভাবে বেসক্লাসের সাথে যোগাযোগ করতে কল করতে পারি? সুপারের মতো?
মিকাজ

-3

গতিশীল বৈশিষ্ট্য মান সহ একটি শ্রেণি তৈরি করতে, নীচের কোডটি চেকআউট করুন। এনবি। এটি পাইথন প্রোগ্রামিং ভাষায় কোড স্নিপেট

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*
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.