python الدوال - الفرق بين الطبقة المجردة والواجهة في بايثون




اوامر اكواد (6)

ما الفرق بين الطبقة المجردة والواجهة في بايثون؟

واجهة ، لكائن ، هي مجموعة من الأساليب والسمات على هذا الكائن.

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

استخدام فئة أساسية Abstract

على سبيل المثال ، لنفترض أننا نريد استخدام إحدى الفئات الأساسية التجريبية من وحدة collections :

import collections
class MySet(collections.Set):
    pass

إذا حاولنا استخدامها ، نحصل على TypeError لأن الفئة التي أنشأناها لا تدعم السلوك المتوقع للمجموعات:

>>> MySet()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: Can't instantiate abstract class MySet with abstract methods
__contains__, __iter__, __len__

لذلك نحن مطالبون بتنفيذ ما لا يقل عن __contains__ ، __iter__ ، و __len__ . لنستخدم مثال التنفيذ هذا من documentation :

class ListBasedSet(collections.Set):
    """Alternate set implementation favoring space over speed
    and not requiring the set elements to be hashable. 
    """
    def __init__(self, iterable):
        self.elements = lst = []
        for value in iterable:
            if value not in lst:
                lst.append(value)
    def __iter__(self):
        return iter(self.elements)
    def __contains__(self, value):
        return value in self.elements
    def __len__(self):
        return len(self.elements)

s1 = ListBasedSet('abcdef')
s2 = ListBasedSet('defghi')
overlap = s1 & s2

التنفيذ: إنشاء فئة أساسية مجردة

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

import abc

على سبيل المثال ، يتم تعريف "effable" على أنه شيء يمكن التعبير عنه بالكلمات. لنقل أننا أردنا تحديد فئة أساسية مجردة قابلة للتنفيذ ، في Python 2:

class Effable(object):
    __metaclass__ = abc.ABCMeta
    @abc.abstractmethod
    def __str__(self):
        raise NotImplementedError('users must define __str__ to use this base class')

أو في Python 3 ، مع التغيير الطفيف في تعريف metaclass:

class Effable(object, metaclass=abc.ABCMeta):
    @abc.abstractmethod
    def __str__(self):
        raise NotImplementedError('users must define __str__ to use this base class')

الآن إذا حاولنا إنشاء كائن قابل للبث بدون تنفيذ الواجهة:

class MyEffable(Effable): 
    pass

ومحاولة لتأكيد ذلك:

>>> MyEffable()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: Can't instantiate abstract class MyEffable with abstract methods __str__

قيل لنا أننا لم ننتهي من العمل.

الآن إذا امتثلنا من خلال توفير الواجهة المتوقعة:

class MyEffable(Effable): 
    def __str__(self):
        return 'expressable!'

يمكننا بعد ذلك استخدام النسخة الملموسة من الطبقة المستمدة من النسخة المجردة:

>>> me = MyEffable()
>>> print(me)
expressable!

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

استنتاج

لقد أثبتنا أن إنشاء فئة أساسية Abstract يُعرّف واجهات للكائنات المخصصة في Python.

ما الفرق بين الطبقة المجردة والواجهة في بايثون؟


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

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

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


Python> = 2.6 يحتوي على فئات أساسية Abstract .

تستكمل الطبقات الأساسية المجردة (ABC المختصرة) كتابة البطة من خلال توفير طريقة لتعريف الواجهات عندما تكون التقنيات الأخرى مثل hasattr () خرقاء. تأتي Python مع العديد من ABCs المبنية لهياكل البيانات (في وحدة المجموعات) ، والأرقام (في وحدة الأرقام) ، والجداول (في وحدة io). يمكنك إنشاء ABC الخاص بك باستخدام وحدة abc.

هناك أيضا وحدة Zope Interface ، التي تستخدمها المشاريع خارج نطاق zope ، مثل الملتوية. أنا لست على دراية به ، ولكن هناك صفحة wiki here قد تساعد.

بشكل عام ، أنت لا تحتاج إلى مفهوم الطبقات المجردة ، أو واجهات في python (تحرير - انظر الإجابة S.Lott للحصول على التفاصيل).


بايثون لا تملك حقا أي من المفهوم.

ويستخدم كتابة بطة ، والتي أزالت الحاجة للواجهات (على الأقل بالنسبة للكمبيوتر :-))

Python <= 2.5: الطبقات الأساسية موجودة بشكل واضح ، ولكن لا توجد طريقة واضحة لتمييز طريقة "افتراضية خالصة" ، وبالتالي فإن الفئة ليست مجردة حقًا.

Python> = 2.6: exist فصول أساسية مجردة ( http://docs.python.org/library/abc.html ). ويسمح لك بتحديد الطرق التي يجب تنفيذها في الفئات الفرعية. أنا لا أحب كثيرا النحو ، ولكن الميزة هناك. في أغلب الأحيان ، من الأفضل استخدام كتابة البط من جانب العميل "استخدام".


ما سترى في بعض الأحيان هو ما يلي:

class Abstract1( object ):
    """Some description that tells you it's abstract,
    often listing the methods you're expected to supply."""
    def aMethod( self ):
        raise NotImplementedError( "Should have implemented this" )

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

والفرق بين المجرد وواجهة هو شيء hairsplitting عندما يكون لديك بطة الكتابة.

تستخدم Java الواجهات لأنها لا تحتوي على ميراث متعدد.

نظرًا لأن بيثون لها ميراث متعدد ، فقد ترى أيضًا شيئًا كهذا

class SomeAbstraction( object ):
    pass # lots of stuff - but missing something

class Mixin1( object ):
    def something( self ):
        pass # one implementation

class Mixin2( object ):
    def something( self ):
        pass # another

class Concrete1( SomeAbstraction, Mixin1 ):
    pass

class Concrete2( SomeAbstraction, Mixin2 ):
    pass

يستخدم هذا نوعًا من الطبقة الفائقة التجريدية مع mixins لإنشاء طبقات فرعية خرسانية منفصلة.


ما الفرق بينstaticmethod وclassmethod في بايثون؟

ربما تكون قد رأيت رمز Python مثل هذا pseudocode ، والذي يوضح توقيعات أنواع الطرق المختلفة ويوفر توثيقًا لشرح كل منها:

class Foo(object):

    def a_normal_instance_method(self, arg_1, kwarg_2=None):
        '''
        Return a value that is a function of the instance with its
        attributes, and other arguments such as arg_1 and kwarg2
        '''

    @staticmethod
    def a_static_method(arg_0):
        '''
        Return a value that is a function of arg_0. It does not know the 
        instance or class it is called from.
        '''

    @classmethod
    def a_class_method(cls, arg1):
        '''
        Return a value that is a function of the class and other arguments.
        respects subclassing, it is called with the class it is called from.
        '''

أسلوب المثيل العادي

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

على سبيل المثال ، هذه نسخة من سلسلة:

', '

إذا استخدمنا طريقة المثيل ، join إلى هذه السلسلة ، للانضمام إلى أمر آخر ، فمن الواضح أنه دالة للمثيل ، بالإضافة إلى كونه دالة للقائمة المتكرّرة ، ['a', 'b', 'c'] :

>>> ', '.join(['a', 'b', 'c'])
'a, b, c'

طرق ملزمة

يمكن ربط أساليب المثيل عبر بحث منقط للاستخدام لاحقًا.

على سبيل المثال ، هذا يربط طريقة str.join ':' :

>>> join_with_colons = ':'.join 

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

>>> join_with_colons('abcde')
'a:b:c:d:e'
>>> join_with_colons(['FF', 'FF', 'FF', 'FF', 'FF', 'FF'])
'FF:FF:FF:FF:FF:FF'

طريقة ثابتة

لا تأخذ الطريقة الثابتة المثيل كوسيطة.

وهو مشابه جدًا لوظيفة مستوى الوحدة النمطية.

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

إذا كان متصلاً بالكائن ، فسيتبع ذلك الشيء بشكل ملائم من خلال الاستيراد والوراثة أيضًا.

مثال على طريقة ثابتة هي str.maketrans ، تم نقلها من الوحدة النمطية string في Python 3. إنها تجعل جدول الترجمة مناسبًا للاستهلاك بواسطة str.translate . يبدو الأمر سخيفًا عند استخدامه من مثيل لسلسلة ، كما هو موضح أدناه ، ولكن استيراد الدالة من الوحدة النمطية string هو بالأحرى خرقاء ، ومن الجيد أن تتمكن من الاتصال به من الفصل ، كما هو الحال في str.maketrans

# demonstrate same function whether called from instance or not:
>>> ', '.maketrans('ABC', 'abc')
{65: 97, 66: 98, 67: 99}
>>> str.maketrans('ABC', 'abc')
{65: 97, 66: 98, 67: 99}

في python 2 ، يجب عليك استيراد هذه الدالة من الوحدة النمطية سلسلة غير مفيدة بشكل متزايد:

>>> import string
>>> 'ABCDEFG'.translate(string.maketrans('ABC', 'abc'))
'abcDEFG'

طريقة الفصل

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

المثال الأكثر اقتراحا من classmethod مدمج هو dict.fromkeys . يتم استخدامه كمنشئ بديل لل dict ، (مناسبة تمامًا عندما تعرف ما هي مفاتيحك وتريد قيمة افتراضية لها.)

>>> dict.fromkeys(['a', 'b', 'c'])
{'c': None, 'b': None, 'a': None}

عندما نستخدم الفئة الفرعية dict ، يمكننا استخدام نفس المُنشئ ، مما يؤدي إلى إنشاء مثيل للفئة الفرعية.

>>> class MyDict(dict): 'A dict subclass, use to demo classmethods'
>>> md = MyDict.fromkeys(['a', 'b', 'c'])
>>> md
{'a': None, 'c': None, 'b': None}
>>> type(md)
<class '__main__.MyDict'>

انظر رمز مصدر الباندا لأمثلة أخرى مماثلة من classmethod البديلة ، وانظر أيضا وثائق بيثون الرسمية على classmethod و staticmethod .







python interface abstract-class