isinstance والسخرية

class HelloWorld(object):
    def say_it(self):
        return 'Hello I am Hello World'

def i_call_hello_world(hw_obj):
    print 'here... check type: %s' %type(HelloWorld)
    if isinstance(hw_obj, HelloWorld):
        print hw_obj.say_it()

from mock import patch, MagicMock
import unittest

class TestInstance(unittest.TestCase):
    @patch('__main__.HelloWorld', spec=HelloWorld)
    def test_mock(self,MK):
        print type(MK)
        MK.say_it.return_value = 'I am fake'
        v = i_call_hello_world(MK)
        print v

if __name__ == '__main__':
    c = HelloWorld()
    i_call_hello_world(c)
    print isinstance(c, HelloWorld)
    unittest.main()

هنا هو التتبع

here... check type: 
Hello I am Hello World
True

here... check type: 
E
======================================================================
ERROR: test_mock (__main__.TestInstance)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/usr/local/lib/python2.7/dist-packages/mock.py", line 1224, in patched
    return func(*args, **keywargs)
  File "t.py", line 18, in test_mock
    v = i_call_hello_world(MK)
  File "t.py", line 7, in i_call_hello_world
    if isinstance(hw_obj, HelloWorld):
TypeError: isinstance() arg 2 must be a class, type, or tuple of classes and types

----------------------------------------------------------------------
Ran 1 test in 0.002s

Q1. Why is this error thrown? They are

0
الآن أنت تعرف لماذا لا يتم تشجيع استخدام isinstance .
وأضاف المؤلف Mark Ransom, مصدر
هذه هي النقطة - واحدة من العديد من الأشياء الجميلة عن بايثون هي أنها تسمح "بطة الكتابة" حيث لا تهتم بالنوع الدقيق لكائن طالما أنها تفعل ما تريد. قد تحتاج إلى توخي الحذر في تسمية الطريقة للتأكد من أنك لا تحدد نفس الاسم بمعنيين مختلفين ، ولكنها تجلب مرونة كبيرة للرمز في النهاية. إنها ميزة ، وليست علة.
وأضاف المؤلف Mark Ransom, مصدر
MarkRansom نعم إنه شر. ولكن ما هي أفضل الممارسات لضمان أن الواجهة التي نجتازها صحيحة؟ لا يبدو أن hasattr يحل الفجوة أيضًا. قد يكون لديك اثنين من الكائنات نفس أسماء الأسلوب ويستخدم كائن خاطئ سيجعل تمرير الاختبار ، على ما أعتقد؟ أعتقد أن تركيز السؤال قد تغير! الخبر.
وأضاف المؤلف CppLearner, مصدر
شكرا مارك. أعتقد أن الموضوع قد تغير. ولكن المشكلة مع الخطأ لم تحل حتى الآن :( سأقدم رسالة جديدة عندما أفكر في سؤال أكثر واقعية فيما يتعلق isinstance. شكرا!
وأضاف المؤلف CppLearner, مصدر
يظهر لك الخطأ لأن HelloWorld (بعد التصحيح) ليس فئة أو نوعًا ، ولكن mock.MagicMock مثيل. كما يقول الخطأ ، يجب أن تكون الوسيطة الثانية فئة ، أو نوع ، أو مجموعة من الفئات أو الأنواع. المواصفات الشيء الذي تشير إلينا لوسيطة أولاً . هذا ما تراه في المثال الأخير (من المستندات). لماذا ، بالضبط ، هل ترغب في التحقق مما إذا كان مثيل HelloWorld الخاص بك هو مثال لنوع يحتذى به (وهو ، على ما أعتقد ، مستحيل)؟
وأضاف المؤلف Magnus Lie Hetland, مصدر
"واحدة من العديد من الأشياء الجميلة حول بايثون هي أنها تسمح" بطة الكتابة "حيث لا تهتم بالنوع الدقيق لكائن طالما أنها تفعل ما تريد" - حتى لا يحدث ذلك.
وأضاف المؤلف spinkus, مصدر

5 إجابة

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

if hasattr(hw_obj, 'say_it'):
    print hw_obj.say_it()

هذا هو تصميم أفضل على أي حال: الاعتماد على نوع المعلومات أكثر هشاشة.

0
وأضاف
لا تستخدم isinstance() ليس حلاً بالنسبة لي: أحاول أن أستهزء بـ datetime.datetime ويتم استخدامه في المكتبات الخارجية. في حالتي django تستخدمه.
وأضاف المؤلف guettli, مصدر
CppLearner نعم ، فهمت ذلك. ما أقصده هو أن بايثون تشجّع عادة كتابة البطّ ، أي ، إذا تصرف جسمك وكأنك تتوقع أن تتصرف ، فلا بأس. لذا ، يجب أن تركز على unittests الخاص بك السلوك. إذا اجتاز شخص ما فصلًا لا يتصرف كما ينبغي ، فيجب أن تفشل.
وأضاف المؤلف Thomas Orozco, مصدر
في العادة ، لا يقتصر الأمر على التحقق من وجود طريقة ما ، بل التحقق من ما إذا كان يعرض ما تتوقعه أم لا.
وأضاف المؤلف Thomas Orozco, مصدر
لا أعرف سبب رمي الخطأ. لا يهم ، سرعان ما لن تستخدم isinstance :) ونعم ، الآن يمكنك تمرير أي كائن له الطريقة ، ويتصرف "بشكل صحيح" ، وسوف يمر الاختبار.
وأضاف المؤلف Ned Batchelder, مصدر
شكر. سأتوقف عن استخدام isinstance بعد قراءة الاحتياط الرئيسية. BUt لا يزال ... إذا كان MyMomKitchenObject لديه say_it ويستخدم مبرمج ذلك كمدخل للوظيفة ... سيظل الاختبار PASS ، أليس كذلك؟ إذن ، كيف يمكنني التحقق من أن أذكر أنني أعمل؟ أو كيف يمكنني تحديد صحتها في الكود؟ تمامًا مثل اختبار التكامل ، يمكن أن يكون للجيبين نفس الواجهة بنسبة 99٪ ، ولا يستخدم النظام قيد الاختبار أبدًا 1٪ مختلفًا ، وما زال الاختبار يمر ، وسيعمل النظام "بدون مشكلة".
وأضاف المؤلف CppLearner, مصدر
شكرا توماس. ولكن هذا ليس ما كنت أسأل. Unittest يفعل التغطية ، واستخدام وهمية يمكن أن تساعدنا على عزل التبعيات من النظام قيد الاختبار. لكننا نريد أن نعرف ما الذي يحدث إذا مرّ شخص ما في الصف X بدلاً من الصنف Y. هل من الممارسة الجيدة التحقق من ذلك في الكود؟ لا أعلم ... سأفكر لفترة ثم أعد رسالة جديدة حول هذا الموضوع.
وأضاف المؤلف CppLearner, مصدر
شكر. لكن السؤال الأول سيكون حقاً لماذا يلقى هذا الخطأ؟ واثنين ، واعتماد هذا التغيير ، إذا كان هناك اثنين من أسماء لها نفس اسم الأسلوب ، سوف يمر الاختبار ، أليس كذلك؟
وأضاف المؤلف CppLearner, مصدر
Downvote ، مما يشير إلى عدم استخدام isinstance هو إجابة رهيبة. افعل ذلك بشكل صحيح باستخدام Mock ، مثل هذا: stackoverflow.com/a/11283173/169153
وأضاف المؤلف Urda, مصدر
CppLearner أنشر إجابة تناسب سؤالك بالضبط.
وأضاف المؤلف Michele d'Amico, مصدر
Upvote. لقد وصلت إلى هذا السؤال لأنني أردت معرفة كيفية اختبار isinstance . أدرك أن إجابة نيد لا تعالج سؤال سياسة العمليات. ومع ذلك ، ساعدني إجابته في التعرف على أن الكود سيكون أفضل مع try بدلاً من isinstance .
وأضاف المؤلف lemi57ssss, مصدر

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

from mock import patch, mock

class Foo(object): pass

# Cache the Foo class so it will be available for isinstance assert.
FooCache = Foo

with patch('__main__.Foo', spec=Foo):
    foo = Foo()
    assert isinstance(foo, FooCache)
    assert isinstance(foo, mock.mock.NonCallableMagicMock)

    # This will cause error from question:
    # TypeError: isinstance() arg 2 must be a class, type, or tuple of classes and types
    assert isinstance(foo, Foo)
0
وأضاف

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

في الرمز ، الخطأ الكبير هو عندما تكتب:

@patch('__main__.HelloWorld', spec=HelloWorld)
def test_mock(self,MK):

من التصحيح الوثائق ، نقرأ ( التأكيد هو لي):

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

وهذا يعني أنه عند تصحيح HelloWorld كائن الفئة سيتم استبدال المرجع إلى HelloWorld بكائن MagicMock سياق test_mock() وظيفة.

ثم ، عندما يتم تنفيذ i_call_hello_world() في إذا كان isinstance (hw_obj، HelloWorld): HelloWorld هو MagicMock() الكائن وليس فئة (كما يوحي الخطأ).

هذا السلوك لأنه كأثر جانبي من تصحيح مرجع فئة تصبح الوسيطة الثانية من isinstance (hw_obj، HelloWorld) كائنًا (مثال MagicMock ). هذه ليست class أو اكتب . تتمثل إحدى التجارب البسيطة لفهم هذا السلوك في تعديل i_call_hello_world() كما يلي:

HelloWorld_cache = HelloWorld

def i_call_hello_world(hw_obj):
    print 'here... check type: %s' %type(HelloWorld_cache)
    if isinstance(hw_obj, HelloWorld_cache):
        print hw_obj.say_it()

سيختفي الخطأ لأن الإشارة الأصلية إلى HelloWorld يتم حفظها في HelloWorld_cache عند تحميل الوحدة. عند تطبيق التصحيح ، سيغير HelloWorld فقط وليس HelloWorld_cache .

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

والخبر السار هو أنه يمكنك القيام بشيء ما ، ولكن لا يمكنك فقط تصحيح مرجع HelloWord في الوحدة النمطية حيث يكون الخاص بك isinstace (o، HelloWord) رمز للاختبار. تعتمد أفضل طريقة على الحالة الحقيقية التي يجب حلها. في المثال الخاص بك ، يمكنك فقط إنشاء Mock لاستخدامه ككائن HelloWorld ، واستخدام spec لارتدائه كـ HelloWorld المثيل واجتياز اختبار isinstance . هذا هو بالضبط أحد الأهداف التي تم تصميم spec بها. سيتم كتابة الاختبار على النحو التالي:

def test_mock(self):
    MK = MagicMock(spec=HelloWorld) #The hw_obj passed to i_call_hello_world
    print type(MK)
    MK.say_it.return_value = 'I am fake'
    v = i_call_hello_world(MK)
    print v

والإخراج من جزء unittest فقط هو


here... check type: 
I am fake
None
0
وأضاف
seanazlin سأتفحصها لاحقًا. ولكن لماذا يقول الخطأ أن HelloWord ليس فئة أو نوع؟ على أي حال شكرا على ملاحظاتك.
وأضاف المؤلف Michele d'Amico, مصدر
SeanAzlin تم تحديثه
وأضاف المؤلف Michele d'Amico, مصدر
SeanAzlin إذا كنت بحاجة إلى توضيحات أخرى ، فيُرجى السؤال ... خلاف ذلك ، قم بإزالة التراجع.
وأضاف المؤلف Michele d'Amico, مصدر
SeanAzlin حلّي الأصلي يقول أنه إذا كان في مكان ما لديك شيء مثل isinstance (o، MyClass) لا يمكنك تصحيح MyClass مرجع isinstance call أي أكثر من ذلك. وهذا يعني أنك بحاجة إلى البحث عن طرق أخرى مثل استخدام كائن زائف مباشرةً.
وأضاف المؤلف Michele d'Amico, مصدر
SeanAzlin ما أعنيه هو أن إخراج type (MagicMock ()) (لذا فإن نوع MegicMock مثيل الكائن ) هو بالضبط < code> . على أي حال ، حتى أنها طبقات فئة أنها ليست فئة ولكن كائن عند محاولة استخدامه في isinstance. تتم إزالة هذا الغموض في python 3 حيث لا تأخذ isinstace فقط أو tuple من اكتب s. في python class ومفاهيم الكائنات قريبة حقاً ... عند استخدام patch يمكنك استبدال المرجع الأصلي بواسطة كائن جديد ( MagicMock كافتراضي) إذا كان هو فئة ... class هي وظائف تعمل على إرجاع
وأضاف المؤلف Michele d'Amico, مصدر
SeanAzlin يرجى كتابة عبارات تتبع وحدة التحكم python الخاصة بك: type (MagicMock ()) ، type (MagicMock) ، type (object ()) ، اكتب (كائن) . بعد ذلك آمل أن تفهموا أن ما كتبته صحيح. على أي حال ، إذا كان تعليقك حول اعتدت كتابة Mock بدلاً من MagicMock ، أعتقد أن هذا خطأ ولكن ليس مشكلة كبيرة جدًا ... إنها مجرد تفاصيل سأفعلها إصلاح. دفع المزيد من الاهتمام عند استخدام downvote. إجابتي صحيحة وهي الوحيدة التي تغطي السؤال الأصلي دون قول * يا رجل! لا تفعل ذلك ".
وأضاف المؤلف Michele d'Amico, مصدر
أعتقد أنني أرى وجهة نظرك. النوع (HelloWorld) بإرجاع classobj ولكن اكتب (MagicMock (spec = HelloWorld)) بإرجاع mock.MagicMock. عند تصحيح الأخطاء مسبقًا في سياق @ mock.patch ، رغم ذلك ، كنت أرى type (HelloWorld) return ، وليس mock .MagicMock </القانون>. كان OP يشاهد الشيء نفسه. يبدو أن الحل الأصلي المكتوب يشير إلى أن الناتج من النوع (الكائن) من يعني أنه سيتم السماح للكائن بالصيغة الثانية من isinstance() عند صرح OP بالفعل أنه ليس كذلك. هل ما زلت أساء فهم الحل الخاص بك؟
وأضاف المؤلف Sean Azlin, مصدر
هذا البيان غير صحيح: "HelloWorld كائن Mock() ولا فئة (كما هو المقترح الخطأ)." إذا التقطت TypeError و debug الأصلي ، فسترى أن تنفيذ type (HelloWorld) سيعرض أيضًا .
وأضاف المؤلف Sean Azlin, مصدر
ميشيل أرى وجهة نظرك الآن. يمكن تلخيص الحل الخاص بك كما يلي: 1) لا تحاول أن تسخر من فئة HelloWorld ، ثم قم بتمريرها كـ arg 2 إلى isinstance . لا يمكن عمل ذلك ، و 2) بدلاً من ذلك ، قم بالمرور في كائن مستهزأ (في نهاية الأمر hw_obj في المثال) ، المصنوع من MagicMock (spec = HelloWorld) ، كالأول إلى isinstance . سوف يمر فحص isinstance. إذا كان هذا هو جوهره ، أعتقد أن الحل الخاص بك هو جيد وسأكون في غاية السعادة +1 ولكن أود أن أوصي التشديد على أن الكائن MagicMock (spec = HelloWorld) يحتاج إلى تمريره إلى isinstance
وأضاف المؤلف Sean Azlin, مصدر

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

class HelloWorld(object):
    def say_it(self):
        return 'Hello I am Hello World'

def i_call_hello_world(hw_obj):
    if isinstance(hw_obj, HelloWorld):
        return hw_obj.say_it()

from mock import patch, MagicMock
import unittest

class TestInstance(unittest.TestCase):
    @patch.object(HelloWorld, 'say_it')
    def test_mock(self, mocked_say_it):
        mocked_say_it.return_value = 'I am fake'
        v = i_call_hello_world(HelloWorld())
        self.assertEquals(v, 'I am fake')
0
وأضاف
مهلا .... أنا أعلم أنه إذا قمت بالنقر فوق السهم الذي لا يصبح 0 ولكن 1. على أي حال كان عليك القيام بالشيء الخطأ ويجب عليك إصلاحه ..... أيا كان
وأضاف المؤلف Michele d'Amico, مصدر
ما لا يمكنك فعله عندما يكون لديك رمز isinstance() هو مجرد تصحيح للفئة ، ولكن يمكنك استخدام محاكاة للكائن. راجع للشغل: انتبه عند استخدام patch.object : استخدمه فقط عندما تحتاج إليه كثيرًا أو الكثير من الوقت لن تفهم سبب عدم عمل التصحيح. (فكِّر في إزالة التراجع عن إجابتي لأن ملاحظتك خاطئة)
وأضاف المؤلف Michele d'Amico, مصدر
ماذا لو كنت بحاجة تسخر من الفصل لأن الكثير من الطرق الداخلية لا يمكن استخدامها في سياق الاختبار؟ السؤال الذي يدور حول المثيل و الهزء و ليس الاستهزاء بالطريقة وأين إجابات Q1 و Q2؟ لا أفسد إجابتك كما فعلت ، لكن ربما تحتاج إلى إيلاء المزيد من الاهتمام للأسئلة قبل تقديم الإجابات. حول إجابتك من وجهة نظر تقنية لماذا تستخدم patch.object لا تحتاج إليها حقًا. يمكنك استخدام patch ('__ main __. HelloWorld.say_it'، return_value = 'I fake') وهو أكثر إيجازًا وأكثر بساطة في القراءة.
وأضاف المؤلف Michele d'Amico, مصدر
@ Micheled'Amico النقاط الجميلة. ما أقترحه هو مجرد حل محتمل (أو ربما يكون المصطلح هو عبارة عن مصطلح أفضل) لبعض الحالات ، مثل عندما تكون وحدة اختبار وظيفة تستخدم الدالة() والتي تستدعي أسلوب فصل يمكن أن " استهزأ بسبب هذه القضية. أعتقد أن العمل قد يكون أكثر جاذبية للبعض من الحلول المقترحة سابقاً. عملت بالنسبة لي في وضعي.
وأضاف المؤلف Sean Azlin, مصدر

يمكنك القيام بذلك عن طريق التوارث من الفئة MagicMock وتجاوز طريقة __ subclasscheck __ :

class BaseMagicMock(MagicMock):

    def __subclasscheck__(self, subclass):
        # I couldn't find another way to get the IDs
        self_id = re.search("id='(.+?)'", self.__repr__()).group(1)
        subclass_id = re.search("id='(.+?)'", subclass.__repr__()).group(1)
        return self_id == subclass_id

ثم يمكنك استخدام هذا الفصل مع decor patch :

class FooBarTestCase(TestCase):
    ...

    @patch('app.services.ClassB', new_callable=BaseMagicMock)
    @patch('app.services.ClassA', new_callable=BaseMagicMock)
    def test_mock_for_issubclass_support(self, ClassAMock, ClassBMock):
        check_for_subclasses(ClassAMock)

هذا هو!




ملاحظات:

يتعين عليك وضع علامة على الفئات كلها التي تتم مقارنتها باستخدام issubclass .

مثال:

def check_for_subclasses(class_1):
    if issubclass(class_1, ClassA): # it's mocked above using BaseMagicMock
        print("This is Class A")
    if issubclass(class_1, ClassB): # it's mocked above using BaseMagicMock
        print("This is Class B")
    if issubclass(class_1, ClassC): # it's not mocked with @patch
        print("This is Class C")

issubclass(class_1, ClassC) will cause an error {TypeError}issubclass() arg 1 must be a class because ClassC contains a default __issubclass__ method. And then we should handle the test like this:

class FooBarTestCase(TestCase):
    ...

    @patch('app.services.ClassC', new_callable=BaseMagicMock)
    @patch('app.services.ClassB', new_callable=BaseMagicMock)
    @patch('app.services.ClassA', new_callable=BaseMagicMock)
    def test_mock_for_issubclass_support(self, ClassAMock, ClassBMock):
        check_for_subclasses(ClassAMock)
0
وأضاف