Why has __new__ been implemented as a static method?

Ian Kelly ian.g.kelly at gmail.com
Sun May 4 13:05:33 EDT 2014


On Sun, May 4, 2014 at 8:16 AM, Steven D'Aprano
<steve+comp.lang.python at pearwood.info> 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.
>
> Whatever the difficulty is with __new__, it isn't something obvious.

You cheated on two counts here.  First, you're using super; I think
Guido's comment about "upcalls" in the link you posted earlier was in
reference to calls that explicitly name the name parent class, i.e.
"dict.fromkeys()", not "super(MyDict, cls).fromkeys()".

Second, you didn't override the method in AnotherDict, so
"MyDict.fromkeys" and "AnotherDict.fromkeys" refer to the same method,
the only difference being in which class is passed to the descriptor
when it is accessed.

Compare to this:

class MyDict(dict):
    @classmethod
    def fromkeys(cls, *args, **kwargs):
        print "MyDict Called from", cls
        return dict.fromkeys(*args, **kwargs)

class AnotherDict(MyDict):
    @classmethod
    def fromkeys(cls, *args, **kwargs):
        print "AnotherDict Called from", cls
        return MyDict.fromkeys(*args, **kwargs)

>>> MyDict.fromkeys('abc')
MyDict Called from <class '__main__.MyDict'>
{'a': None, 'c': None, 'b': None}
>>> AnotherDict.fromkeys('abc')
AnotherDict Called from <class '__main__.AnotherDict'>
MyDict Called from <class '__main__.MyDict'>
{'a': None, 'c': None, 'b': None}



More information about the Python-list mailing list