سؤال

في جاوة، على سبيل المثال، @Override يوفر التعليق التوضيحي فقط التحقق من وقت التحويل الزمني للتجاوز ولكن يجعل للحصول على رمز توثيق ذاتي ممتاز.

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

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

المحلول

تحديث (23.05.2015): بناء على هذا و FWC: S الإجابة قمت بإنشاء حزمة تثبيت PIP https://github.com/mkorpela/overrides.

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

حسنا، ثعبان ليس جافا ولكن بايثون لديها قوة - واضحة أفضل من الضمني - وهناك حالات ملموسة حقيقية في العالم الحقيقي حيث كان هذا الشيء ساعدني.

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

إذا كنت تستطيع التفكير في حل أفضل، فيرجى نشرها هنا!

def overrides(interface_class):
    def overrider(method):
        assert(method.__name__ in dir(interface_class))
        return method
    return overrider

يعمل على النحو التالي:

class MySuperInterface(object):
    def my_method(self):
        print 'hello world!'


class ConcreteImplementer(MySuperInterface):
    @overrides(MySuperInterface)
    def my_method(self):
        print 'hello kitty!'

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

class ConcreteFaultyImplementer(MySuperInterface):
    @overrides(MySuperInterface)
    def your_method(self):
        print 'bye bye!'

>> AssertionError!!!!!!!

نصائح أخرى

فيما يلي تطبيق لا يتطلب مواصفات اسم Interface_Class.

import inspect
import re

def overrides(method):
    # actually can't do this because a method is really just a function while inside a class def'n  
    #assert(inspect.ismethod(method))

    stack = inspect.stack()
    base_classes = re.search(r'class.+\((.+)\)\s*\:', stack[2][4][0]).group(1)

    # handle multiple inheritance
    base_classes = [s.strip() for s in base_classes.split(',')]
    if not base_classes:
        raise ValueError('overrides decorator: unable to determine base class') 

    # stack[0]=overrides, stack[1]=inside class def'n, stack[2]=outside class def'n
    derived_class_locals = stack[2][0].f_locals

    # replace each class name in base_classes with the actual class type
    for i, base_class in enumerate(base_classes):

        if '.' not in base_class:
            base_classes[i] = derived_class_locals[base_class]

        else:
            components = base_class.split('.')

            # obj is either a module or a class
            obj = derived_class_locals[components[0]]

            for c in components[1:]:
                assert(inspect.ismodule(obj) or inspect.isclass(obj))
                obj = getattr(obj, c)

            base_classes[i] = obj


    assert( any( hasattr(cls, method.__name__) for cls in base_classes ) )
    return method

إذا كنت تريد هذا لأغراض التوثيق فقط، فيمكنك تحديد Decorator الخاص بك:

def override(f):
    return f


class MyClass (BaseClass):

    @override
    def method(self):
        pass

هذا ليس حقا سوى حلوى العين، إلا إذا قمت بإنشاء تجاوز (F) بطريقة تشيك في الواقع عن تجاوز.

ولكن بعد ذلك، هذا هو بيثون، لماذا اكتبته كما كان جافا؟

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

أعتقد أن التعليق في DocString هو الكثير. هذا يسمح لأي مستخدم في طريقتك إلى الكتابة help(obj.method) وانظر أن الطريقة هي تجاوز.

يمكنك أيضا توسيع واجهة واضحة class Foo(Interface), ، والتي ستتيح للمستخدمين كتابة help(Interface.method) للحصول على فكرة حول الوظيفة، تهدف الطريقة الخاصة بك إلى توفير.

مثل الآخرين قالوا على عكس جافا، لا توجد علامة overide ولكن أعلاه يمكنك إنشاء الخاصة بك باستخدام الديكور ومع ذلك، أود أن أقترح استخدام الأسلوب العالمي GetATTRIB () بدلا من استخدام الدفات الداخلية لذلك يمكنك الحصول على شيء مثل ما يلي:

def Override(superClass):
    def method(func)
        getattr(superClass,method.__name__)
    return method

إذا كنت تريد أن تتمكن من التقاط GetAttr () في محاولة خاصة بك، قم برفع الخطأ الخاص بك ولكنني أعتقد أن طريقة GetAttr أفضل في هذه الحالة.

أيضا هذا يمسك جميع العناصر المنتصلة إلى فئة بما في ذلك أساليب الفئة والحفر

التقدم في mkorpela. إجابة رائعة, ، إليك نسخة مع

الشيكات أكثر دقة، وتسمية، وكائنات الأخطاء أثارت

def overrides(interface_class):
    """
    Function override annotation.
    Corollary to @abc.abstractmethod where the override is not of an
    abstractmethod.
    Modified from answer https://stackoverflow.com/a/8313042/471376
    """
    def confirm_override(method):
        if method.__name__ not in dir(interface_class):
            raise NotImplementedError('function "%s" is an @override but that'
                                      ' function is not implemented in base'
                                      ' class %s'
                                      % (method.__name__,
                                         interface_class)
                                      )

        def func():
            pass

        attr = getattr(interface_class, method.__name__)
        if type(attr) is not type(func):
            raise NotImplementedError('function "%s" is an @override'
                                      ' but that is implemented as type %s'
                                      ' in base class %s, expected implemented'
                                      ' type %s'
                                      % (method.__name__,
                                         type(attr),
                                         interface_class,
                                         type(func))
                                      )
        return method
    return confirm_override


هنا هو ما يبدو عليه في الممارسة:

NotImplementedError "غير منفذ في الطبقة الأساسية"

class A(object):
    # ERROR: `a` is not a implemented!
    pass

class B(A):
    @overrides(A)
    def a(self):
        pass

النتائج في مزيد من الوصف NotImplementedError خطأ

function "a" is an @override but that function is not implemented in base class <class '__main__.A'>

كومة كاملة

Traceback (most recent call last):
  …
  File "C:/Users/user1/project.py", line 135, in <module>
    class B(A):
  File "C:/Users/user1/project.py", line 136, in B
    @overrides(A)
  File "C:/Users/user1/project.py", line 110, in confirm_override
    interface_class)
NotImplementedError: function "a" is an @override but that function is not implemented in base class <class '__main__.A'>


NotImplementedError "التنفيذ المتوقع"

class A(object):
    # ERROR: `a` is not a function!
    a = ''

class B(A):
    @overrides(A)
    def a(self):
        pass

النتائج في مزيد من الوصف NotImplementedError خطأ

function "a" is an @override but that is implemented as type <class 'str'> in base class <class '__main__.A'>, expected implemented type <class 'function'>

كومة كاملة

Traceback (most recent call last):
  …
  File "C:/Users/user1/project.py", line 135, in <module>
    class B(A):
  File "C:/Users/user1/project.py", line 136, in B
    @overrides(A)
  File "C:/Users/user1/project.py", line 125, in confirm_override
    type(func))
NotImplementedError: function "a" is an @override but that is implemented as type <class 'str'> in base class <class '__main__.A'>, expected implemented type <class 'function'>




الشيء العظيم حول الإجابة @ mkorpela هو الحدث يحدث خلال بعض مراحل التهيئة. الشيك لا يحتاج إلى أن يكون "تشغيل". في اشارة الى الأمثلة السابقة، class B لم تتم تهيئته أبدا (B()) حتى الآن NotImplementedError سوف ترفع. هذا يعنى overrides يتم ضبط الأخطاء عاجلا.

بناء على إجابة @ MKorpela الرائعة، لقد كتبت حزمة مماثلة (أعدك pypi جيثب) هذا يفعل الكثير من الشيكات:

لنفترض أن يرث من B و C. و B Worrits من C. IPROMEISE يتحقق ذلك

  • إذا تجاوز AF BF، يجب أن تكون BF موجودة، ويجب أن ترث من B. (هذا هو الشيك من حزمة التجاوزات).

  • لا تملك النموذج AF يعلن أنه يتجاوز فرنك بلجيكي، والذي يعلن بعد ذلك أنه يتجاوز CF، يجب أن يقول إنه يتجاوز من CF لأن B قد تقرر التوقف عن تجاوز هذه الطريقة، ويجب ألا يؤدي ذلك إلى تحديثات المصب.

  • ليس لديك نمط AF يعلن أنه يتجاوز CF، لكن BF لا يعلن تجاوزه.

  • ليس لديك نمط AF يعلن أنه يتجاوز CF، لكن BF يعلن أنه يتجاوز من بعض DF

كما أن لديها ميزات مختلفة للمناسبة والتحقق من تنفيذ طريقة مجردة.

يسمع أبسط ويعمل تحت Jython مع فصول جافا:

class MyClass(SomeJavaClass):
     def __init__(self):
         setattr(self, "name_of_method_to_override", __method_override__)

     def __method_override__(self, some_args):
         some_thing_to_do()
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top