python - ناطق - معنى كلمة عنوان بالانجليزي




ما هو المزيج ، ولماذا هي مفيدة؟ (10)

في " Python Programming " ، يذكر Mark Lutz "mixins". أنا من خلفية C / C ++ / C # ولم أسمع هذا المصطلح من قبل. ما هو المزيج؟

القراءة بين أسطر هذا المثال (التي ربطتها لأنها طويلة جدًا) ، أفترض أنها حالة استخدام ميراث متعدد لتوسيع الفصل الدراسي بدلاً من الطبقة الفرعية "المناسبة". هل هذا صحيح؟

لماذا أريد القيام بذلك بدلاً من وضع الوظيفة الجديدة في فئة فرعية؟ لهذه المسألة ، لماذا يكون نهج الوراثة بين المزيج / متعددة أفضل من استخدام التركيب؟

ما يفصل mixin من ميراث متعددة؟ هل هي مجرد مسألة دلالات؟


ما يفصل mixin من ميراث متعددة؟ هل هي مجرد مسألة دلالات؟

مزيج هو شكل محدود من الميراث متعددة. في بعض اللغات ، تختلف آلية إضافة ميكسين إلى الفصل قليلاً (من حيث التركيب) من تلك الخاصة بالوراثة.

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

ما قد يجعلك تقول ، "هذا مجرد تعدد الميراث ، وليس مزيجًا حقيقيًا" هو ما إذا كان من الممكن بالفعل استخدام الطبقة التي قد يتم الخلط بينها وبين المزيج ، واستخدامها في الواقع - وهذا في الواقع هو اختلاف دلالية ، وفعلي للغاية.

مثال على الميراث المتعدد

هذا المثال ، من الوثائق ، هو OrderedCounter:

class OrderedCounter(Counter, OrderedDict):
     'Counter that remembers the order elements are first encountered'

     def __repr__(self):
         return '%s(%r)' % (self.__class__.__name__, OrderedDict(self))

     def __reduce__(self):
         return self.__class__, (OrderedDict(self),)

أنه subclasses كل Counter و OrderedDict من وحدة collections .

كل من Counter و OrderedDict يُقصد به أن يتم OrderedDict واستخدامه بمفرده. ومع ذلك ، من خلال تصنيفهم الفرعيين على حد سواء ، يمكن أن يكون لدينا عداد يتم ترتيبه وإعادة استخدامه في كل كائن.

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

مثال على Mixin

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

على عكس المثال أعلاه ، لا يُقصد استخدام المزيج من تلقاء نفسه. يوفر وظائف جديدة أو مختلفة.

على سبيل المثال ، تحتوي المكتبة القياسية على اثنين من mixins في مكتبة socketserver .

يمكن إنشاء إصدارات Forking و threading لكل نوع من الخوادم باستخدام هذه الفئات المختلطة. على سبيل المثال ، يتم إنشاء ThreadingUDPServer كالتالي:

class ThreadingUDPServer(ThreadingMixIn, UDPServer):
    pass

تأتي فئة المزيج أولاً ، لأنها تتجاوز طريقة محددة في UDPServer. يؤدي تعيين السمات المختلفة أيضًا إلى تغيير سلوك آلية الخادم الأساسي.

في هذه الحالة ، تتجاوز أساليب mixin الطرق في تعريف الكائن UDPServer للسماح بالتزامن.

يبدو أن الطريقة التي تم تجاوزها هي process_request كما أنها توفر طريقة أخرى ، process_request_thread . ومن هنا من كود المصدر :

class ThreadingMixIn:
        """Mix-in class to handle each request in a new thread."""

        # Decides how threads will act upon termination of the
        # main process
        daemon_threads = False

        def process_request_thread(self, request, client_address):
            """Same as in BaseServer but as a thread.
            In addition, exception handling is done here.
            """
            try:
                self.finish_request(request, client_address)
            except Exception:
                self.handle_error(request, client_address)
            finally:
                self.shutdown_request(request)

        def process_request(self, request, client_address):
            """Start a new thread to process the request."""
            t = threading.Thread(target = self.process_request_thread,
                                 args = (request, client_address))
            t.daemon = self.daemon_threads
            t.start()

مثال مفتعل

هذا هو المزيج الذي هو في الغالب لأغراض العرض - معظم الكائنات تتطور إلى ما بعد فائدة هذا repr:

class SimpleInitReprMixin(object):
    """mixin, don't instantiate - useful for classes instantiable
    by keyword arguments to their __init__ method.
    """
    __slots__ = () # allow subclasses to use __slots__ to prevent __dict__
    def __repr__(self):
        kwarg_strings = []
        d = getattr(self, '__dict__', None)
        if d is not None:
            for k, v in d.items():
                kwarg_strings.append('{k}={v}'.format(k=k, v=repr(v)))
        slots = getattr(self, '__slots__', None)
        if slots is not None:
            for k in slots:
                v = getattr(self, k, None)
                kwarg_strings.append('{k}={v}'.format(k=k, v=repr(v)))
        return '{name}({kwargs})'.format(
          name=type(self).__name__,
          kwargs=', '.join(kwarg_strings)
          )

والاستخدام سيكون:

class Foo(SimpleInitReprMixin): # add other mixins and/or extend another class here
    __slots__ = 'foo',
    def __init__(self, foo=None):
        self.foo = foo
        super(Foo, self).__init__()

والاستخدام:

>>> f1 = Foo('bar')
>>> f2 = Foo()
>>> f1
Foo(foo='bar')
>>> f2
Foo(foo=None)

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

شاهدت هذا الفيديو http://www.youtube.com/watch?v=v_uKI2NOLEM لفهم أساسيات المزيج. من المفيد تمامًا بالنسبة للمبتدئين فهم أساسيات الخلطات وكيفية عملها والمشاكل التي قد تواجهها في تنفيذها.

لا تزال ويكيبيديا الأفضل: http://en.wikipedia.org/wiki/Mixin


أعتقد أنها طريقة منضبطة لاستخدام الميراث المتعدد - لأن المزيج في نهاية المطاف هو مجرد فئة بيثون أخرى (ربما) تتبع الاتفاقيات حول الطبقات التي تسمى mixins.

إن فهمي للاتفاقيات التي تحكم شيئًا تسمونه Mixin هي أن Mixin:

  • يضيف أساليب ولكن ليس متغيرات الحالة (ثوابت الفصل الدراسي هي موافق)
  • يرث فقط من object (في بايثون)

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

إذا أردت إضافة متغيرات الحالة (مع مرونة أكثر من المسموح بها من قبل الوراثة الفردية) ، فأنا أميل إلى البحث عن التكوين.

بعد قولي هذا ، لقد رأيت فئات تسمى XYZMixin التي لديها متغيرات الحالة.


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

لذلك ، يمكنك استخدام تطبيق milter غير معدَّل ، مثل spfmilter ، واختبار TestBase ، على النحو التالي:

class TestMilter(TestBase,spfmilter.spfMilter):
  def __init__(self):
    TestBase.__init__(self)
    spfmilter.config = spfmilter.Config()
    spfmilter.config.access_file = 'test/access.db'
    spfmilter.spfMilter.__init__(self)

ثم استخدم TestMilter في حالات الاختبار الخاصة بتطبيق milter:

def testPass(self):
  milter = TestMilter()
  rc = milter.connect('mail.example.com',ip='192.0.2.1')
  self.assertEqual(rc,Milter.CONTINUE)
  rc = milter.feedMsg('test1',sender='[email protected]')
  self.assertEqual(rc,Milter.CONTINUE)
  milter.close()

http://pymilter.cvs.sourceforge.net/viewvc/pymilter/pymilter/Milter/test.py?revision=1.6&view=markup


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

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

هناك طرق لجعل عمل MRO متعدد الوراثة في Python ، وأبرزها الدالة super () ، ولكن هذا يعني أنه عليك القيام بهامش صفك بأكمله باستخدام super () ، وهو أكثر صعوبة لفهم تدفق التحكم.


إنه ليس مثالًا على بايثون ، ولكن في لغة البرمجة D ، يُستخدم مصطلح mixin للإشارة إلى البنية المستخدمة بنفس الطريقة ؛ إضافة كومة من الاشياء لفئة.

في D (الذي لا يعمل MI بطريقة ما) ، يتم ذلك عن طريق إدخال قالب (أذكر وحدات الماكرو المدركة من الناحية الحسابية والآمنة وستكون قريبًا) في نطاق. هذا يسمح سطر واحد من التعليمات البرمجية في فئة أو بنية أو دالة أو وحدة نمطية أو ما يتم توسيعه إلى أي عدد من التعريفات.


ربما بعض الأمثلة سيساعد.

إذا كنت تبني فصلًا وتريد أن تتصرف مثل القاموس ، فيمكنك تحديد جميع الطرق المختلفة __ __ اللازمة. لكن هذا شيء من الألم. كبديل ، يمكنك فقط تحديد عدد قليل ، وراثة (بالإضافة إلى أي وراثة أخرى) من UserDict.DictMixin (انتقل إلى collections.DictMixin . DictMixin في py3k). هذا سيكون له تأثير تلقائيا تحديد كل ما تبقى من واجهة برمجة التطبيقات القاموس.

مثال آخر: يسمح لك wxPython في مجموعة أدوات واجهة المستخدم الرسومية (GUI) بإنشاء عناصر تحكم قائمة بأعمدة متعددة (مثل ، عرض الملفات في مستكشف Windows). بشكل افتراضي ، هذه القوائم أساسية إلى حد ما. يمكنك إضافة وظائف إضافية ، مثل القدرة على فرز القائمة حسب عمود معين من خلال النقر على رأس العمود ، من خلال التوارث من ListCtrl وإضافة المزيج المناسب.


ربما مثال من روبي يمكن أن يساعد:

يمكنك تضمين mixin Comparable وتعريف وظيفة واحدة "<=>(other)" ، يوفر mixin كل هذه الوظائف:

<(other)
>(other)
==(other)
<=(other)
>=(other)
between?(other)

يفعل ذلك عن طريق استدعاء <=> <=>(other) وإعطاء النتيجة الصحيحة.

"instance <=> other" يُرجع 0 إذا كان كلتا الكائنات متساويان ، أقل من 0 إذا كان instance أكبر من other وأكثر من 0 إذا كان other أكبر.


مزيج هو نوع خاص من الميراث متعددة. هناك حالتان رئيسيتان تستخدم فيهما الخلطات:

  1. تريد توفير الكثير من الميزات الاختيارية لفئة.
  2. تريد استخدام ميزة واحدة معينة في الكثير من الفصول المختلفة.

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

from werkzeug import BaseRequest

class Request(BaseRequest):
    pass

إذا كنت ترغب في إضافة دعم قبول الرأس ، سأقوم بذلك

from werkzeug import BaseRequest, AcceptMixin

class Request(BaseRequest, AcceptMixin):
    pass

إذا كنت أرغب في إنشاء كائن طلب يدعم قبول الرؤوس ، والأسماء ، والمصادقة ، ودعم وكيل المستخدم ، يمكنني إجراء ذلك:

from werkzeug import BaseRequest, AcceptMixin, ETagRequestMixin, UserAgentMixin, AuthenticationMixin

class Request(BaseRequest, AcceptMixin, ETagRequestMixin, UserAgentMixin, AuthenticationMixin):
    pass

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


يهدف هذا الجواب إلى شرح الاختلاط مع الأمثلة التي هي:

  • مكتفية ذاتيا : قصيرة ، دون الحاجة إلى معرفة أي مكتبات لفهم المثال.

  • في بايثون ، وليس في اللغات الأخرى.

    من المفهوم أن هناك أمثلة من لغات أخرى مثل روبي لأن المصطلح أكثر شيوعًا في هذه اللغات ، لكن هذا هو موضوع بايثون .

كما يجب عليه أن ينظر في المسألة المثيرة للجدل:

هل الوراثة المتعددة ضرورية أم لا لتمييز الخلط؟

تعريفات

لم أر حتى الآن اقتباسًا من مصدر "موثوق" يقول بوضوح ما هو المزيج في بايثون.

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

قد يختلف الإجماع بين لغات مختلفة.

التعريف 1: لا يوجد ميراث متعدد

المزيج هو فئة بحيث تستخدم بعض أساليب الفصل طريقة غير محددة في الفصل.

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

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

مثال تقليدي هو تنفيذ جميع عوامل المقارنة من فقط <= و == :

class ComparableMixin(object):
    """This class has methods which use `<=` and `==`,
    but this class does NOT implement those methods."""
    def __ne__(self, other):
        return not (self == other)
    def __lt__(self, other):
        return self <= other and (self != other)
    def __gt__(self, other):
        return not self <= other
    def __ge__(self, other):
        return self == other or self > other

class Integer(ComparableMixin):
    def __init__(self, i):
        self.i = i
    def __le__(self, other):
        return self.i <= other.i
    def __eq__(self, other):
        return self.i == other.i

assert Integer(0) <  Integer(1)
assert Integer(0) != Integer(1)
assert Integer(1) >  Integer(0)
assert Integer(1) >= Integer(1)

# It is possible to instantiate a mixin:
o = ComparableMixin()
# but one of its methods raise an exception:
#o != o 

هذا المثال على وجه الخصوص كان يمكن تحقيقه عبر الديكور functools.total_ordering() ، ولكن اللعبة هنا كانت لإعادة اختراع العجلة:

import functools

@functools.total_ordering
class Integer(object):
    def __init__(self, i):
        self.i = i
    def __le__(self, other):
        return self.i <= other.i
    def __eq__(self, other):
        return self.i == other.i

assert Integer(0) < Integer(1)
assert Integer(0) != Integer(1)
assert Integer(1) > Integer(0)
assert Integer(1) >= Integer(1)

التعريف 2: ميراث متعدد

المزيج هو نمط تصميم تستخدم فيه بعض طرق الطبقة الأساسية طريقة لا تعرّفها ، وتهدف هذه الطريقة إلى تنفيذها بواسطة طبقة أساسية أخرى ، وليس بواسطة المشتق في التعريف 1.

يشير المصطلح mixin class إلى الفصول الأساسية التي يقصد استخدامها في نمط التصميم هذا (TODO تلك التي تستخدم هذه الطريقة ، أو تلك التي تنفذها؟)

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

هذا النمط مثير للاهتمام لأنه من الممكن إعادة تجميع الوظائف باختيارات مختلفة للفئات الأساسية:

class HasMethod1(object):
    def method(self):
        return 1

class HasMethod2(object):
    def method(self):
        return 2

class UsesMethod10(object):
    def usesMethod(self):
        return self.method() + 10

class UsesMethod20(object):
    def usesMethod(self):
        return self.method() + 20

class C1_10(HasMethod1, UsesMethod10): pass
class C1_20(HasMethod1, UsesMethod20): pass
class C2_10(HasMethod2, UsesMethod10): pass
class C2_20(HasMethod2, UsesMethod20): pass

assert C1_10().usesMethod() == 11
assert C1_20().usesMethod() == 21
assert C2_10().usesMethod() == 12
assert C2_20().usesMethod() == 22

# Nothing prevents implementing the method
# on the base class like in Definition 1:

class C3_10(UsesMethod10):
    def method(self):
        return 3

assert C3_10().usesMethod() == 13

تواجدات بيثون الموثوقة

في documentatiton الرسمي للمجموعات . ABC تستخدم الوثائق بشكل صريح مصطلح طرق Mixin .

تنص على أنه إذا كان الفصل الدراسي:

  • تنفذ __next__
  • يرث من Iterator فئة واحدة

ثم يحصل الطبقة على طريقة mixin __iter__ مجانا.

لذلك على الأقل في هذه النقطة من الوثائق ، لا يتطلب mixin تعدد الميراث ، وهو متماسك مع التعريف 1.

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

تستخدم هذه الصفحة أيضًا مصطلح Set mixin ، مما يشير بوضوح إلى أن فئات مثل Set و Iterator يمكن أن تسمى فئات Mixin.

بلغات أخرى

  • روبي: من الواضح أنه لا يتطلب ميراثاً متعدداً للمزيج ، كما هو مذكور في الكتب المرجعية الرئيسية مثل برمجة روبي و لغة برمجة روبي

  • C ++: تعتبر الطريقة التي لم يتم تنفيذها طريقة افتراضية خالصة.

    يتطابق التعريف 1 مع تعريف فئة تجريدية (فئة لها طريقة افتراضية خالصة). لا يمكن استنساخ تلك الفئة.

    التعريف 2 ممكن مع الميراث الظاهري: الوراثة المتعددة من فئتين مشتقتين





mixins