Path: csiph.com!v102.xanadu-bbs.net!xanadu-bbs.net!goblin1!goblin2!goblin.stu.neva.ru!newsfeed.xs4all.nl!newsfeed4.news.xs4all.nl!xs4all!newsgate.cistron.nl!newsgate.news.xs4all.nl!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; 'argument': 0.05; 'assign': 0.07; 'attribute': 0.07; 'arguments': 0.09; 'assumed': 0.09; 'explanation': 0.09; 'implements': 0.09; 'received:80.91': 0.09; 'received:80.91.229': 0.09; 'received:gmane.org': 0.09; 'received:list': 0.09; 'subject:instance': 0.09; 'variable,': 0.09; 'python': 0.11; 'def': 0.12; 'stored': 0.12; 'creates': 0.14; 'arg': 0.16; 'argument.': 0.16; 'gently': 0.16; 'received:80.91.229.3': 0.16; 'received:dip0.t-ipconnect.de': 0.16; 'received:plane.gmane.org': 0.16; 'received:t-ipconnect.de': 0.16; 'set,': 0.16; 'subject:class': 0.16; 'subject:variable': 0.16; 'underlying': 0.16; 'wrote:': 0.18; 'variable': 0.18; 'mechanism': 0.19; '>>>': 0.22; 'header:User-Agent:1': 0.23; 'passes': 0.24; 'please?': 0.24; "i've": 0.25; 'equivalent': 0.26; 'pass': 0.26; 'post': 0.26; 'header:X-Complaints-To:1': 0.27; 'tried': 0.27; 'function': 0.29; "i'm": 0.30; '>>>>': 0.31; 'object.': 0.31; 'class': 0.32; 'subject:from': 0.34; 'could': 0.34; "can't": 0.35; 'but': 0.35; 'method': 0.36; 'two': 0.37; 'implement': 0.38; 'to:addr:python-list': 0.38; 'explain': 0.39; 'itself': 0.39; 'to:addr:python.org': 0.39; 'skip:p 20': 0.39; 'received:org': 0.40; 'called': 0.40; 'first': 0.61; "you'll": 0.62; 'provide': 0.64; 'here': 0.66; 'inform': 0.78; 'ref': 0.84 X-Injected-Via-Gmane: http://gmane.org/ To: python-list@python.org From: Peter Otten <__peter__@web.de> Subject: Re: odd difference calling function from class or instance variable Date: Wed, 13 Aug 2014 11:40:18 +0200 Organization: None References: <201408131006549222-not@myrealaddresscom> Mime-Version: 1.0 Content-Type: text/plain; charset="ISO-8859-1" Content-Transfer-Encoding: 7Bit X-Gmane-NNTP-Posting-Host: p57bdb2d6.dip0.t-ipconnect.de User-Agent: KNode/4.13.2 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: 77 NNTP-Posting-Host: 2001:888:2000:d::a6 X-Trace: 1407922838 news.xs4all.nl 2922 [2001:888:2000:d::a6]:53554 X-Complaints-To: abuse@xs4all.nl Xref: csiph.com comp.lang.python:76179 GregS wrote: > Hello, > > This is my first post here so please gently inform me of any etiquette > breaches. > > I'm seeing a behaviour I can't explain with Python 3.4.1 when I call a > function via a reference stored in an object. > > When I assign the reference as a class variable, the reference has > __self__ set, too, so I get an extra argument passed to the function. > If I assign the reference as an instance variable, then __self__ is > unset so no extra argument. > > Here's what I mean: > >>>> def print_args(*args): > print(args) > >>>> class C: > ref = None > >>>> C.ref = print_args # assign to class variable >>>> i = C() >>>> i.ref() # call via class variable - get a 'self' argument passed > (<__main__.C object at 0x1071a05f8>,) >>>> i.ref = print_args # assign to instance variable >>>> i.ref() # call via instance variable: no arguments > () > > If you look at i.ref.__self__ for the two cases, you'll see what's > going on. I've tried RTFMing but can't find the reason for the two > behaviours. Could someone provide an explanation for me, please? When an attribute is found in the instance it is left as-is, so i.ref() is the same as print_ref() When the attribute is found in the class and itself has a __get__ attribute i.ref() is equivalent to print_ref.__get__(i, C)() which creates a bound method object (i. e. it is assumed that the function implements a method): >>> class C: pass ... >>> def f(self): pass ... >>> f.__get__(C(), C) > As you have seen a bound method implicitly passes the instance as the first arg to the function. The underlying mechanism is called "descriptor protocol" and is also used to implement properties. If you need to store a function in the class you can wrap it as a staticmethod: >>> def print_args(*args): print(args) ... >>> class C: ... ref = staticmethod(print_args) ... >>> C().ref() ()