Strange classmethod mock behavior

Fabio Zadrozny fabiofz at gmail.com
Tue Oct 25 18:59:26 EDT 2011


On Tue, Oct 25, 2011 at 10:08 AM, Peter Otten <__peter__ at web.de> wrote:
> Fabio Zadrozny wrote:
>>> I'm trying to mock a classmethod in a superclass but when restoring it
>> a strange behavior happens in subclasses (tested on Python 2.5)
>>>> Anyone knows why this happens? (see test case below for details)
>> Is there any way to restore that original method to have the original
>> behavior?
>>>> import unittest
>>>> class Test(unittest.TestCase):
>>>>     def testClassmethod(self):
>>         class Super():
>>             @classmethod
>>             def GetCls(cls):
>>                 return cls
>>>>         class Sub(Super):
>>             pass
>>>>         self.assertEqual(Sub.GetCls(), Sub)
>>>>         original = Super.GetCls
>>         #Mock Super.GetCls, and do tests...
>>         Super.GetCls = original #Restore the original classmethod
>>         self.assertEqual(Sub.GetCls(), Sub) #The call to the
>> classmethod subclass returns the cls as Super and not Sub as expected!
>>>> if __name__ == '__main__':
>>     unittest.main()
>> [Not related to your problem] When working with descriptors it's a good idea
> to use newstyle classes, i. e. have Super inherit from object.
>> The Super.GetCls attribute access is roughly equivalent to
>> Super.__dict___["GetCls"].__get__(classmethod_instance, None, Super)
>> and returns an object that knows about its class. So when you think you are
> restoring the original method you are actually setting the GetCls attribute
> to something else. You can avoid the problem by accessing the attribute
> explicitly:
>>>>> class Super(object):
> ...     @classmethod
> ...     def m(cls): return cls
> ...
>>>> bad = Super.m
>>>> good = Super.__dict__["m"]
>>>> class Sub(Super): pass
> ...
>>>> Sub.m()
> <class '__main__.Sub'>
>>>> Super.m = bad
>>>> Sub.m()
> <class '__main__.Super'>
>>>> Super.m = good
>>>> Sub.m()
> <class '__main__.Sub'>
>
Hi Peter, thanks for the explanation.
Printing it helped me understand it even better...
print(Super.__dict__['GetCls'])
print(Super.GetCls)
<classmethod object at 0x022AF210>
<bound method type.GetCls of <class '__main__.Super'>>
Cheers,
Fabio


More information about the Python-list mailing list

AltStyle によって変換されたページ (->オリジナル) /