بايثون :فئة فرعية` نوع ' لإنشاء أنواع متخصصة (على سبيل المثال."قائمة كثافة العمليات")

StackOverflow https://stackoverflow.com/questions/6332342

  •  27-10-2019
  •  | 
  •  

سؤال

أحاول أن فئة فرعية type من أجل إنشاء فئة تسمح ببناء أنواع متخصصة.على سبيل المثال.a ListType :

>>> ListOfInt = ListType(list, value_type=int)
>>> issubclass(ListOfInt, list)
True
>>> issubclass(list, ListOfInt)
False
>>> # And so on ...

ومع ذلك ، هذا ListOfInt لن يتم استخدامها لإنشاء مثيلات !أنا فقط استخدامه كمثال على type يمكنني التلاعب للمقارنة مع الأنواع الأخرى ...على وجه الخصوص ، في حالتي أنا بحاجة إلى البحث عن عملية مناسبة ، وفقا لنوع المدخلات ، وأنا بحاجة إلى نوع لاحتواء المزيد من التوضيحات (مثل list of int أو XML string, ، إلخ ...).

حتى هنا ما خطرت لي :

class SpzType(type):

    __metaclass__ = abc.ABCMeta

    @classmethod
    def __subclasshook__(cls, C):
        return NotImplemented

    def __new__(cls, base, **features):
        name = 'SpzOf%s' % base.__name__
        bases = (base,)
        attrs = {}
        return super(SpzType, cls).__new__(cls, name, bases, attrs)

    def __init__(self, base, **features):
        for name, value in features.items():
            setattr(self, name, value)

استخدام abc ليس واضحا في التعليمات البرمجية أعلاه ...ولكن إذا كنت ترغب في كتابة فئة فرعية ListType كما هو الحال في المثال في الأعلى ، يصبح مفيدا ...

تعمل الوظيفة الأساسية بالفعل :

>>> class SimpleType(SpzType): pass
>>> t = SimpleType(int)
>>> issubclass(t, int)
True
>>> issubclass(int, t)
False

ولكن عندما أحاول التحقق مما إذا كان t هو مثال على SpzType, بيثون النزوات :

>>> isinstance(t, SpzType)
TypeError: __subclasscheck__() takes exactly one argument (0 given)

لقد استكشفت مع pdb.pm() ما كان يحدث ، ووجدت أن التعليمات البرمجية التالية يثير الخطأ :

>>> SpzType.__subclasscheck__(SimpleType)
TypeError: __subclasscheck__() takes exactly one argument (0 given)

غريب ?!من الواضح أن هناك حجة ...إذن ماذا يعني ذلك ?أي فكرة ?هل أساءت استخدام abc ?

هل كانت مفيدة؟

المحلول 3

شكرا للتعليق من كيندال, ، لقد ريفاكتوريد التعليمات البرمجية إلى ما يلي :

class SpzType(abc.ABCMeta):

    def __subclasshook__(self, C):
        return NotImplemented

    def __new__(cls, base, **features):
        name = 'SpzOf%s' % base.__name__
        bases = (base,)
        attrs = {}
        new_spz = super(SpzType, cls).__new__(cls, name, bases, attrs)
        new_spz.__subclasshook__ = classmethod(cls.__subclasshook__)
        return new_spz

    def __init__(self, base, **features):
        for name, value in features.items():
            setattr(self, name, value)

لذلك أساسا, SpzType هو الآن فئة فرعية من abc.ABCMeta, ، و الفئة الفرعيةهوك يتم تنفيذه كطريقة مثيل.إنه يعمل بشكل رائع وهو (إيمو) أنيق !!!

تحرير :كان هناك شيء صعب ...لأن __subclasshook__ يجب أن يكون كلاسميثود ، لذلك لا بد لي من استدعاء وظيفة كلاسميثود يدويا...وإلا فإنه لا يعمل إذا كنت ترغب في تنفيذ __subclasshook__.

نصائح أخرى

لست متأكدا تماما ما تريد تحقيقه.ربما من الأفضل استخدام collections وحدة بدلا من استخدام abc مباشرة?

هناك المزيد من المعلومات حول فئات المجموعة العامة في بيب 3119

من المحتمل أن يتم نوع الشيء الذي تريد القيام به بسهولة أكبر باستخدام وظيفة مصنع فئة مثل ما يلي.على الأقل بالنسبة لي ، فإنه يجعل من أكثر وضوحا للحفاظ على التوالي مختلف المستويات التي أحاول أن تعمل.

def listOf(base, types={}, **features):
    key = (base,) + tuple(features.items())
    if key in types:
        return types[key]
    else:

        if not isinstance(base, type):
            raise TypeError("require element type, got '%s'" % base)

        class C(list):

             def __init__(self, iterable=[]):
                 for item in iterable:
                     try:    # try to convert to desired type
                         self.append(self._base(item))
                     except ValueError:
                         raise TypeError("value '%s' not convertible to %s"
                            % (item, self._base.__name__))

              # similar methods to type-check other list mutations

        C.__name__ = "listOf(%s)" % base.__name__
        C._base = base
        C.__dict__.update(features)  
        types[key] = C
        return C

لاحظ أنني أستخدم ملف dict كذاكرة تخزين مؤقت هنا بحيث تحصل على نفس كائن الفئة لمجموعة معينة من نوع العنصر والميزات.هذا يجعل listOf(int) is listOf(int) دائما True.

وهنا نسخة الديكور من إجابتي الأخرى التي تعمل مع أي فئة.يقوم المصمم بإرجاع دالة مصنع تقوم بإرجاع فئة فرعية من الفئة الأصلية بالسمات المطلوبة.الشيء الجميل في هذا النهج هو أنه لا يفرض ميتاكلاس ، حتى تتمكن من استخدام ميتاكلاس (على سبيل المثال. ABCMeta) إذا رغبت في ذلك دون صراعات.

لاحظ أيضا أنه إذا كانت الفئة الأساسية تستخدم فئة ميتاكلاس ، فسيتم استخدام فئة ميتاكلاس لإنشاء مثيل للفئة الفرعية التي تم إنشاؤها.هل يمكن ، إذا أردت ، من الصعب رمز ميتاكلاس المطلوب ، أو ، كما تعلمون ، كتابة الديكور الذي يجعل ميتاكلاس في الديكور لفئات القالب...انها الديكور على طول الطريق!

إذا كان موجودا ، طريقة فئة __classinit__() يتم تمرير الحجج التي تم تمريرها إلى المصنع ، وبالتالي فإن الفئة نفسها يمكن أن يكون رمز للتحقق من صحة الحجج وتعيين سماتها.(هذا من شأنه أن يسمى بعد ميتاكلاس __init__().) إذا __classinit__() إرجاع فئة ، يتم إرجاع هذه الفئة من قبل المصنع بدلا من واحد ولدت ، حتى تتمكن من تمديد حتى الإجراء جيل بهذه الطريقة (على سبيل المثال.بالنسبة لفئة قائمة محددة من النوع ، يمكنك إرجاع إحدى فئتين داخليتين اعتمادا على ما إذا كان يجب إجبار العناصر على نوع العنصر أم لا).

إذا __classinit__() غير موجود ، يتم تعيين الحجج التي تم تمريرها إلى المصنع ببساطة كسمات فئة على الفئة الجديدة.

للراحة في إنشاء فئات حاوية مقيدة بالنوع ، تعاملت مع نوع العنصر بشكل منفصل عن ديكت الميزة.إذا لم يتم تمريره ، فسيتم تجاهله.

كما كان من قبل ، يتم تخزين الفئات التي تم إنشاؤها بواسطة المصنع مؤقتا بحيث تحصل في كل مرة تستدعي فيها فئة بنفس الميزات على نفس مثيل كائن الفئة.

def template_class(cls, classcache={}):

    def factory(element_type=None, **features):

        key = (cls, element_type) + tuple(features.items())
        if key in classcache:
            return classcache[key]

        newname  = cls.__name__
        if element_type or features:
            newname += "("
            if element_type:
                newname += element_type.__name__
                if features:
                    newname += ", "
            newname += ", ".join(key + "=" + repr(value)
                                 for key, value in features.items())
            newname += ")"

        newclass = type(cls)(newname, (cls,), {})
        if hasattr(newclass, "__classinit__"):
            classinit = getattr(cls.__classinit__, "im_func", cls.__classinit__)
            newclass = classinit(newclass, element_type, features) or newclass
        else:
            if element_type:
                newclass.element_type = element_type
            for key, value in features.items():
                setattr(newclass, key, value)

        classcache[key] = newclass
        return newclass

    factory.__name__ = cls.__name__
    return factory

مثال على فئة قائمة مقيدة بالنوع (تحويل النوع ، في الواقع):

@template_class
class ListOf(list):

    def __classinit__(cls, element_type, features):
        if isinstance(element_type, type):
            cls.element_type = element_type
        else:
            raise TypeError("need element type")

    def __init__(self, iterable):
        for item in iterable:
            try:
                self.append(self.element_type(item))
            except ValueError:
                raise TypeError("value '%s' not convertible to %s"
                        % (item, self.element_type.__name__))

    # etc., to provide type conversion for items added to list 

توليد فئات جديدة:

Floatlist = ListOf(float)
Intlist   = ListOf(int)

ثم إنشاء مثيل:

print FloatList((1, 2, 3))       # 1.0, 2.0, 3.0
print IntList((1.0, 2.5, 3.14))  # 1, 2, 3

أو مجرد إنشاء فئة وإنشاء مثيل في خطوة واحدة:

print ListOf(float)((1, 2, 3))
print ListOf(int)((1.0, 2.5, 3.14))
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top