Path: csiph.com!v102.xanadu-bbs.net!xanadu-bbs.net!feeder.erje.net!eu.feeder.erje.net!xlned.com!feeder7.xlned.com!news2.euro.net!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; 'subject:not': 0.03; '(even': 0.05; 'attribute': 0.07; 'detect': 0.07; 'builtin': 0.09; 'function,': 0.09; 'subject:question': 0.10; 'cc:addr:python- list': 0.11; 'python': 0.11; 'def': 0.12; '(#1,': 0.16; 'argument:': 0.16; 'attribute,': 0.16; 'callable': 0.16; 'callable,': 0.16; 'cc:name:python list': 0.16; 'eckhardt': 0.16; 'inst': 0.16; 'nameerror:': 0.16; 'positional': 0.16; 'stumbled': 0.16; 'typeerror:': 0.16; 'unexpected': 0.16; 'x()': 0.16; 'wrote:': 0.18; 'variable': 0.18; 'thu,': 0.19; '>>>': 0.22; 'example': 0.22; 'aug': 0.22; 'cc:addr:python.org': 0.22; '>>>': 0.24; 'cc:2**0': 0.24; '2.0': 0.26; 'pass': 0.26; 'header:In-Reply-To:1': 0.27; 'am,': 0.29; 'skip:@ 10': 0.30; 'message-id:@mail.gmail.com': 0.30; "skip:' 10": 0.31; '"",': 0.31; 'subject:that': 0.31; 'file': 0.32; 'class': 0.32; 'linux': 0.33; '(most': 0.33; 'subject:the': 0.34; 'test': 0.35; 'but': 0.35; 'received:google.com': 0.35; 'there': 0.35; 'accessing': 0.36; 'method': 0.36; 'example,': 0.37; 'expected': 0.38; 'skip:& 10': 0.38; '8bit%:4': 0.38; 'jason': 0.38; 'list,': 0.38; 'recent': 0.39; 'skip:& 20': 0.39; 'skip:x 10': 0.40; 'even': 0.60; 'back': 0.62; 'more': 0.64; 'jul': 0.74; 'behavior': 0.77; '2013,': 0.91; '2013': 0.98 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=mime-version:in-reply-to:references:date:message-id:subject:from:to :cc:content-type; bh=2WRyTxiEhCTNkNEIl2lK8ZRkP9EPdcODvceB1v/Twww=; b=ErGXG/FPq9mp1uXqsxgdz3ave4RP4raUsd7Cm/O2aQBl4bRnqPxaxZdBD/SujYHdmr JI+TPz28xBzTyxoPaCQgJvLCTLV+larK9hbrLSaCFKNg/atJdFs+/vswReg+8shFXsqP YyB76G+Zm0B0poGf1nemAoeZY1//em405BafHdyuCOSy5imZFAaWIfcNTyEnU7bvWt/U V07v14zdan9CyOiwipu1cwis4x1c16JWfpX1QX3QU3ZC69RR63nMGiQVjIkyJs1w5osz MghnW8MI5T+NGp0KI1owq17reySrRBNnjFW1TS64pRGYNjk87BaVTG8iOlnGO6gjKYDY 34Ug== MIME-Version: 1.0 X-Received: by 10.42.36.198 with SMTP id v6mr11253636icd.58.1373552914787; Thu, 11 Jul 2013 07:28:34 -0700 (PDT) In-Reply-To: References: Date: Thu, 11 Jul 2013 10:28:34 -0400 Subject: Re: Callable or not callable, that is the question! From: Jason Swails To: Ulrich Eckhardt Content-Type: multipart/alternative; boundary=001a11c3a2b090fc2104e13d35b8 Cc: python list 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: 192 NNTP-Posting-Host: 2001:888:2000:d::a6 X-Trace: 1373552924 news.xs4all.nl 15878 [2001:888:2000:d::a6]:37772 X-Complaints-To: abuse@xs4all.nl Xref: csiph.com comp.lang.python:50448 --001a11c3a2b090fc2104e13d35b8 Content-Type: text/plain; charset=ISO-8859-1 On Thu, Jul 11, 2013 at 9:05 AM, Ulrich Eckhardt < ulrich.eckhardt@dominolaser.com> wrote: > Hello! > > I just stumbled over a case where Python (2.7 and 3.3 on MS Windows) fail > to detect that an object is a function, using the callable() builtin > function. Investigating, I found out that the object was indeed not > callable, but in a way that was very unexpected to me: > > class X: > @staticmethod > def example(): > pass > test1 = example > test2 = [example,] > > X.example() # OK > X.test1() # OK > X.test2[0]() # TypeError: 'staticmethod' object is not callable > Interestingly, you can actually use this approach to 'fake' staticmethod before staticmethod was even introduced. By accessing example from inside the test2 class attribute list, there is no instance bound to that method (even if an instance was used to access it). Using Python 3.3: Python 3.3.2 (default, Jun 3 2013, 08:29:09) [GCC 4.5.4] on linux Type "help", "copyright", "credits" or "license" for more information. >>> class X: ... def example(): pass ... test = example, ... >>> X.test[0]() >>> Using Python 2.0 (pre-staticmethod): Python 2.0.1 (#1, Aug 28 2012, 20:25:41) [GCC 4.5.3] on linux3 Type "copyright", "credits" or "license" for more information. >>> class X: ... def example(): pass ... test = example, ... >>> X.test[0]() >>> staticmethod Traceback (most recent call last): File "", line 1, in ? NameError: There is no variable named 'staticmethod' Once you change test into an instance attribute, you get back to the expected behavior Python 3.3.2 (default, Jun 3 2013, 08:29:09) [GCC 4.5.4] on linux Type "help", "copyright", "credits" or "license" for more information. >>> class X: ... def example(self): pass ... test = example, ... >>> inst = X() >>> inst.example() >>> inst.test[0]() Traceback (most recent call last): File "", line 1, in TypeError: example() missing 1 required positional argument: 'self' >>> inst.test = inst.example, >>> inst.test[0]() >>> All the best, Jason --001a11c3a2b090fc2104e13d35b8 Content-Type: text/html; charset=ISO-8859-1 Content-Transfer-Encoding: quoted-printable


On Th= u, Jul 11, 2013 at 9:05 AM, Ulrich Eckhardt <ulrich.eckhar= dt@dominolaser.com> wrote:
Hello!

I just stumbled over a case where Python (2.7 and 3.3 on MS Windows) fail t= o detect that an object is a function, using the callable() builtin functio= n. Investigating, I found out that the object was indeed not callable, but = in a way that was very unexpected to me:

=A0 =A0 class X:
=A0 =A0 =A0 =A0 @staticmethod
=A0 =A0 =A0 =A0 def example():
=A0 =A0 =A0 =A0 =A0 =A0 pass
=A0 =A0 =A0 =A0 test1 =3D example
=A0 =A0 =A0 =A0 test2 =3D [example,]

=A0 =A0 X.example() # OK
=A0 =A0 X.test1() # OK
=A0 =A0 X.test2[0]() # TypeError: 'staticmethod' object is not call= able

Interestingly, you can actually use this approach to = 9;fake' staticmethod before staticmethod was even introduced. =A0By acc= essing example from inside the test2 class attribute list, there is no inst= ance bound to that method (even if an instance was used to access it).

Using Python 3.3:

Python 3.3.2 (default,= Jun =A03 2013, 08:29:09)=A0
[GCC 4.5.4] on linux
Type "help", "copyright", "credits" or "= license" for more information.
>>> class X:
... =A0 =A0 def example(): pass
... =A0 =A0 test =3D example,=
...=A0
>>> X.test[0]()
>>>=A0

Using Python 2.0 (pre-staticmethod):

Python 2.0.1 (#1, Aug = 28 2012, 20:25:41)=A0
[GCC 4.5.= 3] on linux3
Type "copyright", "credits" or "license" f= or more information.
>>> class X:<= /font>
... def example(): pass
... test =3D example,=A0
...=A0
>>> X.test[0]()
>>> staticmet= hod
Traceb= ack (most recent call last):
=A0 File "<stdin>", line 1, in ?<= /div>
NameError: There is no= variable named 'staticmethod'

Once you change test in= to an instance attribute, you get back to the expected behavior

Python 3.3.2 (default, Jun =A03= 2013, 08:29:09)=A0
[GCC 4.5.4] on linux
Type "help",= "copyright", "credits" or "license" for more= information.
>>> class X:
... =A0 =A0 def exampl= e(self): pass
... =A0 =A0 test =3D example,=A0
...=A0
>>> inst =3D = X()
>&g= t;> inst.example()
>>> inst.test[0]()
Traceback (most recent= call last):
=A0 File "<stdin>", line 1, in <module>
TypeError: example() missing 1 required positional = argument: 'self'
>>> inst.test =3D inst.example,=A0
>>> inst.test[0]()
>>>=A0

All the best,
= Jason
--001a11c3a2b090fc2104e13d35b8--