Why has __new__ been implemented as a static method?

Rotwang sg552 at hotmail.co.uk
Sun May 4 12:24:11 EDT 2014


On 04/05/2014 15:16, Steven D'Aprano wrote:
> On Sun, 04 May 2014 20:03:35 +1200, Gregory Ewing wrote:
>
>> Steven D'Aprano wrote:
>>> If it were a class method, you would call it by MyBaseClass.__new__()
>>> rather than explicitly providing the cls argument.
>>
>> But that wouldn't be any good, because the base __new__ needs to receive
>> the actual class being instantiated, not the class that the __new__
>> method belongs to.
>
>
> Which is exactly what method descriptors -- whether instance methods or
> class descriptors -- can do. Here's an example, using Python 2.7:
>
> class MyDict(dict):
>      @classmethod
>      def fromkeys(cls, *args, **kwargs):
>          print "Called from", cls
>          return super(MyDict, cls).fromkeys(*args, **kwargs)
>
> class AnotherDict(MyDict):
>      pass
>
>
> And in use:
>
> py> MyDict.fromkeys('abc')
> Called from <class '__main__.MyDict'>
> {'a': None, 'c': None, 'b': None}
> py> AnotherDict().fromkeys('xyz')
> Called from <class '__main__.AnotherDict'>
> {'y': None, 'x': None, 'z': None}
>
>
> In both cases, MyDict's __new__ method receives the class doing the
> calling, not the class where the method is defined.

Yes, when a classmethod bound to a subclass or an instance is called. 
But this is irrelevant to Gregory's point:

On 04/05/2014 04:37, Steven D'Aprano wrote:
> On Sun, 04 May 2014 11:21:53 +1200, Gregory Ewing wrote:
>> Steven D'Aprano wrote:
>>> I'm not entirely sure what he means by "upcalls", but I believe it
>>> means to call the method further up (that is, closer to the base) of
>>> the inheritance tree.
>>
>> I think it means this:
>>
>>      def __new__(cls):
>>         MyBaseClass.__new__(cls)
>>
>> which wouldn't work with a class method, because MyBaseClass.__new__
>> would give a *bound* method rather than an unbound one.
>
> If it were a class method, you would call it by MyBaseClass.__new__()
> rather than explicitly providing the cls argument.


The relevant behaviour is this:

 >>> class C:
     @classmethod
     def m(cls):
         print("Called from", cls)

 >>> class D(C):
     @classmethod
     def m(cls):
         C.m()

		
 >>> C.m()
Called from <class '__main__.C'>
 >>> D.m()
Called from <class '__main__.C'>


If __new__ were a classmethod, then a call to MyBaseClass.__new__() 
within the body of MySubClass.__new__ would pass MyBaseClass to the 
underlying function, not the MySubClass. This means that

class MySubClass(MyBaseClass):
     def __new__(cls):
         return MyBaseClass.__new__()

would fail, since it would return an instance of MyBaseClass rather than 
MySubClass.



More information about the Python-list mailing list