Path: csiph.com!v102.xanadu-bbs.net!xanadu-bbs.net!news.mixmin.net!rt.uk.eu.org!newsfeed.xs4all.nl!newsfeed4.news.xs4all.nl!xs4all!post.news.xs4all.nl!not-for-mail Return-Path: X-Original-To: python-list@python.org Delivered-To: python-list@mail.python.org X-Spam-Status: OK 0.000 X-Spam-Evidence: '*H*': 1.00; '*S*': 0.00; 'receives': 0.04; 'explicitly': 0.05; 'class,': 0.07; 'none,': 0.07; '*args,': 0.09; 'method,': 0.09; 'override': 0.09; 'subject:Why': 0.09; 'subject:method': 0.09; 'python': 0.11; 'def': 0.12; "wouldn't": 0.14; 'posted': 0.15; '**kwargs)': 0.16; '**kwargs):': 0.16; '2.7:': 0.16; '@classmethod': 0.16; '__new__': 0.16; 'argument.': 0.16; 'belongs': 0.16; 'cls': 0.16; 'defined.': 0.16; 'descriptor': 0.16; 'descriptors': 0.16; "guido's": 0.16; "{'a':": 0.16; 'wrote:': 0.18; 'do.': 0.18; '>>>': 0.22; 'print': 0.22; 'earlier': 0.24; 'compare': 0.26; 'first,': 0.26; 'this:': 0.26; 'pass': 0.26; 'skip:" 20': 0.27; 'header:In-Reply-To:1': 0.27; 'am,': 0.29; 'message-id:@mail.gmail.com': 0.30; "d'aprano": 0.31; 'second,': 0.31; 'steven': 0.31; 'class': 0.32; 'actual': 0.34; 'comment': 0.34; 'skip:d 20': 0.34; 'something': 0.35; 'but': 0.35; 'received:google.com': 0.35; 'i.e.': 0.36; 'doing': 0.36; "didn't": 0.36; 'method': 0.36; 'subject:?': 0.36; 'example,': 0.37; 'two': 0.37; 'being': 0.38; 'whatever': 0.38; 'to:addr :python-list': 0.38; 'rather': 0.38; 'to:addr:python.org': 0.39; 'called': 0.40; 'providing': 0.61; "you're": 0.61; 'name': 0.63; 'refer': 0.63; 'difficulty': 0.68; 'receive': 0.70; 'counts': 0.83; 'accessed.': 0.84; 'obvious.': 0.84; 'good,': 0.91 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=mime-version:in-reply-to:references:from:date:message-id:subject:to :content-type; bh=1thZVlFO7YGqZZ7j8KAuOIWE2LN4tF6A5z7CwN64iIk=; b=cU4EGmAt5cdHEsqDZd3BuLHFzYwrgHnMShKVW0C1JqBDwFHoZFUACEl2itPAqyAici /TZzQjp+Or4yQLTk9/FXJe7NAyFzR5cIto1UkFsxq/0b78J2qn9wf3XvviAXkBlo9NCt ySAEXqUjvoZ96aD//Nuhnm1tmm7fk/ixsTazmGXSQ1o7/SkPCQwHGxPjLyxs5KABa6Pa Qlb51MOJvi+5U4YhXZhCTsnXlKbKGyfoM1u007rryERiFVyl+AB8luI1yfJXKvgO8Ebg hpdzLI+pqblX7M+z1s6AEqKFoqzpLxDEct9CsBrCr9eizSkl6d6EB4WJJJhpMf+U4OrS RdAQ== X-Received: by 10.67.13.134 with SMTP id ey6mr20231886pad.44.1399223174042; Sun, 04 May 2014 10:06:14 -0700 (PDT) MIME-Version: 1.0 In-Reply-To: <53664b9f$0$29965$c3e8da3$5496439d@news.astraweb.com> References: <53650751$0$29965$c3e8da3$5496439d@news.astraweb.com> <5365b5f1$0$29965$c3e8da3$5496439d@news.astraweb.com> <53664b9f$0$29965$c3e8da3$5496439d@news.astraweb.com> From: Ian Kelly Date: Sun, 4 May 2014 11:05:33 -0600 Subject: Re: Why has __new__ been implemented as a static method? To: Python Content-Type: text/plain; charset=UTF-8 X-BeenThere: python-list@python.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: General discussion list for the Python programming language List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Newsgroups: comp.lang.python Message-ID: Lines: 72 NNTP-Posting-Host: 2001:888:2000:d::a6 X-Trace: 1399223177 news.xs4all.nl 2939 [2001:888:2000:d::a6]:57740 X-Complaints-To: abuse@xs4all.nl Xref: csiph.com comp.lang.python:70914 On Sun, May 4, 2014 at 8:16 AM, 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 > {'a': None, 'c': None, 'b': None} > py> AnotherDict().fromkeys('xyz') > Called from > {'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 {'a': None, 'c': None, 'b': None} >>> AnotherDict.fromkeys('abc') AnotherDict Called from MyDict Called from {'a': None, 'c': None, 'b': None}