ما هي بعض حالات الاستخدام (الخرسانة) للبلاستيك؟

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

  •  23-08-2019
  •  | 
  •  

سؤال

لدي صديق يحب استخدام Metaclasses، ويوفر لهم بانتظام كحل.

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

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

لذا، من فضلك، أنا يائس لمعرفة حالات الاستخدام الصالحة (الخرسانة) الخاصة بك للبلاسيز في بيثون. أو أن تكون مستنيرا لماذا تكون فئات التحور أفضل من كائنات الطيار، وأحيانا.

سأبدأ:

في بعض الأحيان عند استخدام مكتبة جهة خارجية، فمن المفيد أن تكون قادرا على تحريض الفصل بطريقة معينة.

(هذه هي الحالة الوحيدة التي يمكنني التفكير فيها، وهي ليست ملموسة)

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

المحلول

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

class PlottingInteractive:
    add_slice = wrap_pylab_newplot(add_slice)

لا تتوافق هذه الطريقة مع تغييرات API وهكذا، ولكن واحدة تكرر على سمات الفصل __init__ قبل إعادة تعيين سمات الفصل هي أكثر كفاءة وتحتفظ بالأشياء محدثة:

class _Interactify(type):
    def __init__(cls, name, bases, d):
        super(_Interactify, cls).__init__(name, bases, d)
        for base in bases:
            for attrname in dir(base):
                if attrname in d: continue # If overridden, don't reset
                attr = getattr(cls, attrname)
                if type(attr) == types.MethodType:
                    if attrname.startswith("add_"):
                        setattr(cls, attrname, wrap_pylab_newplot(attr))
                    elif attrname.startswith("set_"):
                        setattr(cls, attrname, wrap_pylab_show(attr))

بالطبع، قد تكون هناك طرق أفضل للقيام بذلك، لكنني وجدت أن هذا فعال. بالطبع، يمكن أيضا القيام بذلك في __new__ أو __init__, ، ولكن هذا كان الحل الذي وجدته أكثر وضوحا.

نصائح أخرى

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

معظم metaclasses التي رأيتها تفعل واحدة من شيئين:

  1. التسجيل (إضافة فئة إلى بنية بيانات):

    models = {}
    
    class ModelMetaclass(type):
        def __new__(meta, name, bases, attrs):
            models[name] = cls = type.__new__(meta, name, bases, attrs)
            return cls
    
    class Model(object):
        __metaclass__ = ModelMetaclass
    

    كلما كنت الفئة الفرعية Model, ، صفك مسجل في models قاموس:

    >>> class A(Model):
    ...     pass
    ...
    >>> class B(A):
    ...     pass
    ...
    >>> models
    {'A': <__main__.A class at 0x...>,
     'B': <__main__.B class at 0x...>}
    

    ويمكن أيضا أن يتم ذلك مع ديكورات الفصل:

    models = {}
    
    def model(cls):
        models[cls.__name__] = cls
        return cls
    
    @model
    class A(object):
        pass
    

    أو مع وظيفة التسجيل الصريح:

    models = {}
    
    def register_model(cls):
        models[cls.__name__] = cls
    
    class A(object):
        pass
    
    register_model(A)
    

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

    على أي حال، ميزة Metaclasses في هذه الحالة هي الميراث، لأنها تعمل في أي فرعية، في حين أن الحلول الأخرى تعمل فقط من أجل الفئات الفرعية المزخرفة أو المسجلة بشكل صريح.

    >>> class B(A):
    ...     pass
    ...
    >>> models
    {'A': <__main__.A class at 0x...> # No B :(
    
  2. إعادة تكوين (تعديل سمات الطبقة أو إضافة جديدة):

    class ModelMetaclass(type):
        def __new__(meta, name, bases, attrs):
            fields = {}
            for key, value in attrs.items():
                if isinstance(value, Field):
                    value.name = '%s.%s' % (name, key)
                    fields[key] = value
            for base in bases:
                if hasattr(base, '_fields'):
                    fields.update(base._fields)
            attrs['_fields'] = fields
            return type.__new__(meta, name, bases, attrs)
    
    class Model(object):
        __metaclass__ = ModelMetaclass
    

    كلما كنت الفئة الفرعية Model وتحديد بعض Field السمات، يتم حقنها بأسمائها (للحصول على رسائل خطأ أكثر إعلامية، على سبيل المثال)، وتجميعها في _fields قاموس (للتكرار السهل، دون الحاجة إلى النظر من خلال جميع سمات الفصل وجميع سمات الطبقات الأساسية في كل مرة):

    >>> class A(Model):
    ...     foo = Integer()
    ...
    >>> class B(A):
    ...     bar = String()
    ...
    >>> B._fields
    {'foo': Integer('A.foo'), 'bar': String('B.bar')}
    

    مرة أخرى، يمكن القيام بذلك (بدون ميراث) مع ديكور فئة:

    def model(cls):
        fields = {}
        for key, value in vars(cls).items():
            if isinstance(value, Field):
                value.name = '%s.%s' % (cls.__name__, key)
                fields[key] = value
        for base in cls.__bases__:
            if hasattr(base, '_fields'):
                fields.update(base._fields)
        cls._fields = fields
        return cls
    
    @model
    class A(object):
        foo = Integer()
    
    class B(A):
        bar = String()
    
    # B.bar has no name :(
    # B._fields is {'foo': Integer('A.foo')} :(
    

    أو صراحة:

    class A(object):
        foo = Integer('A.foo')
        _fields = {'foo': foo} # Don't forget all the base classes' fields, too!
    

    على الرغم من ذلك، على عكس الدعوة الخاصة بك ببرمجة غير قابلة للقراءة وغير واضحة، فإن هذا أكثر مرهقة ومرهدة وعرضة للخطأ والخطأ:

    class B(A):
        bar = String()
    
    # vs.
    
    class B(A):
        bar = String('bar')
        _fields = {'B.bar': bar, 'A.foo': A.foo}
    

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

class Metaclass(type):
    def __new__(meta, name, bases, attrs):
        return type.__new__(meta, 'foo', (int,), attrs)

class Baseclass(object):
    __metaclass__ = Metaclass

class A(Baseclass):
    pass

class B(A):
    pass

print A.__name__ # foo
print B.__name__ # foo
print issubclass(B, A)   # False
print issubclass(B, int) # True

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

على أي حال، في بيثون 3، metaclasses أيضا __prepare__ الطريقة، التي تتيح لك تقييم جسم الفصل في رسم خرائط بخلاف dict, ، وبالتالي دعم السمات المطلوبة، السمات المثقلة، وغيرها من الأشياء الرائعة الأشرار:

import collections

class Metaclass(type):

    @classmethod
    def __prepare__(meta, name, bases, **kwds):
        return collections.OrderedDict()

    def __new__(meta, name, bases, attrs, **kwds):
        print(list(attrs))
        # Do more stuff...

class A(metaclass=Metaclass):
    x = 1
    y = 2

# prints ['x', 'y'] rather than ['y', 'x']

 

class ListDict(dict):
    def __setitem__(self, key, value):
        self.setdefault(key, []).append(value)

class Metaclass(type):

    @classmethod
    def __prepare__(meta, name, bases, **kwds):
        return ListDict()

    def __new__(meta, name, bases, attrs, **kwds):
        print(attrs['foo'])
        # Do more stuff...

class A(metaclass=Metaclass):

    def foo(self):
        pass

    def foo(self, x):
        pass

# prints [<function foo at 0x...>, <function foo at 0x...>] rather than <function foo at 0x...>

قد تجادل السمات المطلوبة يمكن تحقيقها مع عدادات الخلق، ويمكن محاكاة التحميل الزائد مع الوسائط الافتراضية:

import itertools

class Attribute(object):
    _counter = itertools.count()
    def __init__(self):
        self._count = Attribute._counter.next()

class A(object):
    x = Attribute()
    y = Attribute()

A._order = sorted([(k, v) for k, v in vars(A).items() if isinstance(v, Attribute)],
                  key = lambda (k, v): v._count)

 

class A(object):

    def _foo0(self):
        pass

    def _foo1(self, x):
        pass

    def foo(self, x=None):
        if x is None:
            return self._foo0()
        else:
            return self._foo1(x)

إلى جانب كونها أكثر قباشرة، فإنها أقل مرونة أيضا: ماذا لو كنت تريد السمات الحرفية المطلوبة، مثل الأعداد الصحيحة والسلاسل؟ ماذا إذا None هي قيمة صالحة ل x?

إليك طريقة إبداعية لحل المشكلة الأولى:

import sys

class Builder(object):
    def __call__(self, cls):
        cls._order = self.frame.f_code.co_names
        return cls

def ordered():
    builder = Builder()
    def trace(frame, event, arg):
        builder.frame = frame
        sys.settrace(None)
    sys.settrace(trace)
    return builder

@ordered()
class A(object):
    x = 1
    y = 'foo'

print A._order # ['x', 'y']

وهنا طريقة إبداعية لحل الثانية:

_undefined = object()

class A(object):

    def _foo0(self):
        pass

    def _foo1(self, x):
        pass

    def foo(self, x=_undefined):
        if x is _undefined:
            return self._foo0()
        else:
            return self._foo1(x)

ولكن هذا كثير، الكثير من الفودو-إيه من metaclass بسيطة (خاصة أول واحد، الذي يذوب عقلك حقا). تتمثل وجهة نظري في ذلك، فأنت تنظر إلى Metaclasses بأنها غير مألوفة وبديهية بديهية، ولكن يمكنك أيضا أن تنظر إليهم كخطوة أخرى للتطور في لغات البرمجة: عليك فقط ضبط عقليتك. بعد كل شيء، ربما يمكنك القيام بكل شيء في C، بما في ذلك تحديد الهيكل بمؤشرات الوظائف ويمرها كوسيطة الأولى لوظائفها. الشخص الذي يرى C ++ لأول مرة قد يقول، "ما هذا السحر؟ لماذا يمر مترجم ضمنيا this إلى الأساليب، ولكن ليس للوظائف العادية والثابتة؟ من الأفضل أن تكون صريحا وقذيرا حول حججك ". ولكن بعد ذلك، فإن البرمجة الموجهة للكائنات أقوى بكثير بمجرد حصولها؛ وهكذا هل هذا، إيه ... برمجة موجهة نحو جانب جوانب، وأعتقد. فهم Metaclasses، إنها بالفعل بسيطة للغاية، فلماذا لا تستخدمها عند مريحة؟

وأخيرا، فإن Metaclasses هي Rad، والبرمجة يجب أن تكون ممتعة. باستخدام بنيات البرمجة القياسية وأنماط التصميم طوال الوقت مملة وغير ملهمة، وتعيق خيالك. عش قليلا! إليك metametaclass، فقط بالنسبة لك.

class MetaMetaclass(type):
    def __new__(meta, name, bases, attrs):
        def __new__(meta, name, bases, attrs):
            cls = type.__new__(meta, name, bases, attrs)
            cls._label = 'Made in %s' % meta.__name__
            return cls 
        attrs['__new__'] = __new__
        return type.__new__(meta, name, bases, attrs)

class China(type):
    __metaclass__ = MetaMetaclass

class Taiwan(type):
    __metaclass__ = MetaMetaclass

class A(object):
    __metaclass__ = China

class B(object):
    __metaclass__ = Taiwan

print A._label # Made in China
print B._label # Made in Taiwan

الغرض من Metaclasses لا يحل محل تمييز الفصل / الكائن مع Metaclass / Class - إنه لتغيير سلوك تعريفات الفئة (وبالتالي مثيلاتهم) بطريقة ما. بشكل فعال، من أجل تغيير سلوك بيان الفصل بطرق قد تكون أكثر فائدة للنطاق الخاص بك من الافتراضي. الأشياء التي استخدمتها لهم هي:

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

    class Mp3File(MusicFile):
        extensions = ['.mp3']  # Register this type as a handler for mp3 files
        ...
        # Implementation of mp3 methods go here
    

    Metaclass ثم يحافظ على قاموس {'.mp3' : MP3File, ... } إلخ، وتبني كائنا من النوع المناسب عند طلب معالج من خلال وظيفة المصنع.

  • تغيير السلوك. قد ترغب في إرفاق معنى خاص لسمات معينة، مما يؤدي إلى تغيير السلوك عندما تكون موجودة. على سبيل المثال، قد ترغب في البحث عن طرق مع الاسم _get_foo و _set_foo وتحويلها بشفافية إلى العقارات. كمثال في العالم الحقيقي، هنا وصفة كتبت لإعطاء المزيد من تعريفات الهيكل مثل C. يتم استخدام Metaclass لتحويل العناصر المعلنة إلى سلسلة تنسيق الهيكل، والمناولة الميراث، إلخ، وإنتاج فئة قادرة على التعامل معها.

    للحصول على أمثلة أخرى في العالم الحقيقي، نلقي نظرة على مختلف الأورام، مثل Sqlalchemy's orm أو sqlobject.. وبعد مرة أخرى، فإن الغرض منه هو تفسير Defintions (هنا تعريفات عمود SQL) مع معنى معين.

دعنا نبدأ مع Quote Tim Peter الكلاسيكي:

Metaclasses هي سحر أعمق من 99٪ من المستخدمين يجب أن تقلق من أي وقت مضى. إذا كنت تتساءل عما إذا كنت في حاجة إليهم، فأنت لا (الأشخاص الذين يحتاجون إليهم في الواقع يعرفون أنه يحتاجون إليهم، ولا يحتاجون إلى تفسير حول السبب). تيم بيترز (CLP Post 2002-12-22)

بعد القول، لدي (بشكل دوري) تشغيل عبر الاستخدامات الحقيقية للبلاستياس. الشخص الذي يتبادر إلى الذهن هو في Django حيث يرث كل نماذجك من النماذج. Model. models.model، بدوره، يفعل بعض السحر الخطير لالتفاف نماذج DB الخاصة بك مع جماعة Django orm. يحدث هذا السحر عن طريق metaclasses. يخلق جميع أنواع فصول الاستثناءات، دروس المدير، إلخ.

انظر django / db / النماذج / base.py، modelbase الفئة () لبداية القصة.

يمكن أن تكون Metaclasses مفيدة للبناء لغات المجال الخاصة في Python. أمثلة ملموسة هي بناء جملة قاعدة بيانات SQLObjo، SQLOBJECTS |

مثال أساسي من metaclass المحافظ بواسطة إيان بيكينغ:

كانت الملصقات التي استخدمتها في المقام الأول لدعم نوع من الأسلوب التصريحي للبرمجة. على سبيل المثال، النظر في مخطط التحقق من الصحة:

class Registration(schema.Schema):
    first_name = validators.String(notEmpty=True)
    last_name = validators.String(notEmpty=True)
    mi = validators.MaxLength(1)
    class Numbers(foreach.ForEach):
        class Number(schema.Schema):
            type = validators.OneOf(['home', 'work'])
            phone_number = validators.PhoneNumber()

بعض التقنيات الأخرى: المكونات لبناء DSL في بيثون (بي دي إف).

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

number_validator = [
    v.OneOf('type', ['home', 'work']),
    v.PhoneNumber('phone_number'),
]

validators = [
    v.String('first_name', notEmpty=True),
    v.String('last_name', notEmpty=True),
    v.MaxLength('mi', 1),
    v.ForEach([number_validator,])
]

انها ليست مثالية، ولكن بالفعل هناك ما يقرب من صفر السحر، لا حاجة للبلاستيك، وتحسين التوحيد.

يقوم نمط معقول من استخدام Metaclass يفعل شيئا بمجرد تحديد فئة بدلا من مرارا وتكرارا كلما تم إنشاء مثيل نفس الفئة.

عندما تشترك فئات متعددة في نفس السلوك الخاص، كرر __metaclass__=X من الواضح أن أفضل من تكرار رمز الأغراض الخاصة و / أو إدخال الفائقة المشتركة مخصصة.

ولكن حتى مع فئة واحدة خاصة فقط ولا تمديد متوقع، __new__ و __init__ من Metaclass هي طريقة نظافة لتهيئة متغيرات الفئة أو بيانات عالمية أخرى من رمز التداخل الخاص والأغراض الخاصة def و class البيانات في الجسم تعريف الصف.

المرة الوحيدة التي استخدمتها metaclasses في بيثون كانت عند كتابة غلاف ل API Flickr.

كان هدفي كشط موقع واجهة برمجة تطبيقات فليكر وتوليد ديناميكيا التسلسل الهرمي للفئة كاملة للسماح بوصول API باستخدام كائنات Python:

# Both the photo type and the flickr.photos.search API method 
# are generated at "run-time"
for photo in flickr.photos.search(text=balloons):
    print photo.description

لذلك في ذلك المثال، نظرا لأنني قمت بإنشاء Python Flickr API بأكمله من الموقع، فأنا لا أعرف حقا تعريفات الفصل في وقت التشغيل. أن تكون قادرا على إنشاء أنواع ديناميكيا مفيدة للغاية.

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

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

class test(baseclass_with_metaclass):
    method_maker_value = "hello"

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

الحاجة إلى ذلك أمر نادرا للغاية على الرغم من أنها تقطع قليلا فقط من الكتابة التي لا تستحق النظر فيها حقا ما لم يكن لديك رمز كبير بما فيه الكفاية.

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

ومع ذلك، قد يكون الأمر مفيدا جدا في Smalltalk و Ruby لتكون قادرا على تعديل فئة موجودة، لكن بيثون لا يحب أن يفعل ذلك مباشرة.

هناك ممتاز مجرى المطور على metaclassing في بيثون قد يساعد. ال ويكيبيديا المادة هو أيضا جيدة جدا.

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

انهم مثل الملح. من السهل استخدام الكثير.

الطريقة التي استخدمت بها metaclasses هي توفير بعض السمات للفصول الدراسية. خذ هذا المثال:

class NameClass(type):
    def __init__(cls, *args, **kwargs):
       type.__init__(cls, *args, **kwargs)
       cls.name = cls.__name__

سوف نضع اسم السمة على كل فئة سيكون لها تعيين metaclass للإشارة إلى nameclass.

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

القدرة على إعادة كتابة مكتبة بأكملها ديناميكيا بسلاسة بحيث تعمل بشكل صحيح كما هو متوقع في تطبيق مؤشر متعدد الاستخدامات مفيدة للغاية في بعض الحالات. ال safetkinter. الوحدة التي تفعل ذلك بمساعدة metaclass المقدمة من قبل خيط الوحدة - الأحداث والأوقات غير مطلوبة.

جانب واحد أنيق من threadbox هو أنه لا يهتم بأي نوع من الحيوانات المستنسخة. يوفر مثالا على كيفية لمس جميع الفئات الأساسية بواسطة Metaclass إذا لزم الأمر. فائدة أخرى تأتي مع metaclasses هي أنها تعمل على ميراث الفصول أيضا. البرامج التي تكتب أنفسهم - لماذا لا؟

حالة الاستخدام الشرعي الوحيد لميتكلاس هو الحفاظ على مطوري فضوليين آخرين من لمس التعليمات البرمجية الخاصة بك. بمجرد أن يقوم Mastaclasses Metaclasses Metaclass في المطور بالفوز ويبدأ بديل معك أو رمي مستوى آخر أو اثنين لإبعادهم. إذا كان هذا لا يعمل، ابدأ في استخدام type.__new__ أو ربما بعض المخطط باستخدام metaclass العودية.

(لسان مكتوب في الخد، لكنني رأيت هذا النوع من التعويضات. Django مثال مثالي)

هذا هو استخدام بسيط، ولكن ... شيء واحد وجدته metaclasses مفيدة له هو استدعاء وظيفة كلما تم إنشاء فئة فرعية. قمت بإنفاذ هذا في metaclass الذي يبحث عن __initsubclass__ السمة: كلما تم إنشاء فئة فرعية، يتم استدعاء جميع فئات الأم والذي حدد هذه الطريقة مع __initsubclass__(cls, subcls). وبعد يسمح ذلك بإنشاء فئة الوالدين التي يسجل بعد ذلك جميع الفئات الفرعية مع بعض السجل العالمي، ويتم تشغيل الشيكات الثابتة على الفئات الفرعية كلما تم تعريفها، وإجراء عمليات ملزمة متأخرة، إلخ ... كل ذلك دون أن يتصل يدويا وظائف يدويا أو لإنشاء Metaclass مخصص أداء كل من هذه الواجبات المنفصلة.

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

اضطررت مؤخرا إلى استخدام metaclass للمساعدة في تحديد الإعلان بشكل تصفيح نموذج SQLAlchemy حول جدول قاعدة البيانات مأهولة ببيانات التعداد الأمريكي من http://census.ire.org/data/bulkdata.html.

ire يوفر قذائف قاعدة البيانات بالنسبة للجداول بيانات التعداد، والتي تنشئ أعمدة عدد صحيح بعد اتفاقية تسمية من مكتب الإحصاء P012015، P012016، P012017، إلخ.

أردت أن أكون قادرا على الوصول إلى هذه الأعمدة باستخدام model_instance.p012017 بناء الجملة، ب) أن تكون واضحة إلى حد ما حول ما كنت أفعله و C) لا تضطر إلى تحديد العشرات بشكل صريح من الحقول على النموذج، لذلك فهي فرعية sqlalchemy DeclarativeMeta للتكرار من خلال مجموعة من الأعمدة وإنشاء الحقول النموذجية تلقائيا المقابلة للأعمدة:

from sqlalchemy.ext.declarative.api import DeclarativeMeta

class CensusTableMeta(DeclarativeMeta):
    def __init__(cls, classname, bases, dict_):
        table = 'p012'
        for i in range(1, 49):
            fname = "%s%03d" % (table, i)
            dict_[fname] = Column(Integer)
            setattr(cls, fname, dict_[fname])

        super(CensusTableMeta, cls).__init__(classname, bases, dict_)

يمكنني بعد ذلك استخدام هذه metaclass لتعريف النموذج الخاص بي والوصول إلى الحقول المرفعة تلقائيا في النموذج:

CensusTableBase = declarative_base(metaclass=CensusTableMeta)

class P12Tract(CensusTableBase):
    __tablename__ = 'ire_p12'

    geoid = Column(String(12), primary_key=True)

    @property
    def male_under_5(self):
        return self.p012003

    ...

يبدو أن هناك استخدام شرعي موصوف هنا - إعادة كتابة بوثون دوكاتينز مع metaclass.

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

https://docs.python.org/3/Reference/datamodel.html#mleaclass-example.

ولكن بشكل عام: تقييم بعناية فائقة، إذا كنت حقا بحاجة حقا إلى التعقيد الإضافي للبلاستيك.

الجواب من dan gittik هو بارد

يمكن أن توضح الأمثلة في النهاية أشياء كثيرة، لقد غيرتها إلى بيثون 3 وإعطاء بعض التفسير:

class MetaMetaclass(type):
    def __new__(meta, name, bases, attrs):
        def __new__(meta, name, bases, attrs):
            cls = type.__new__(meta, name, bases, attrs)
            cls._label = 'Made in %s' % meta.__name__
            return cls

        attrs['__new__'] = __new__
        return type.__new__(meta, name, bases, attrs)

#China is metaclass and it's __new__ method would be changed by MetaMetaclass(metaclass)
class China(MetaMetaclass, metaclass=MetaMetaclass):
    __metaclass__ = MetaMetaclass

#Taiwan is metaclass and it's __new__ method would be changed by MetaMetaclass(metaclass)
class Taiwan(MetaMetaclass, metaclass=MetaMetaclass):
    __metaclass__ = MetaMetaclass

#A is a normal class and it's __new__ method would be changed by China(metaclass)
class A(metaclass=China):
    __metaclass__ = China

#B is a normal class and it's __new__ method would be changed by Taiwan(metaclass)
class B(metaclass=Taiwan):
    __metaclass__ = Taiwan


print(A._label)  # Made in China
print(B._label)  # Made in Taiwan

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

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

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

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top