Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.python > #50441 > unrolled thread

Callable or not callable, that is the question!

Started byUlrich Eckhardt <ulrich.eckhardt@dominolaser.com>
First post2013-07-11 15:05 +0200
Last post2013-07-12 01:11 -0600
Articles 9 — 6 participants

Back to article view | Back to comp.lang.python


Contents

  Callable or not callable, that is the question! Ulrich Eckhardt <ulrich.eckhardt@dominolaser.com> - 2013-07-11 15:05 +0200
    Re: Callable or not callable, that is the question! Peter Otten <__peter__@web.de> - 2013-07-11 16:11 +0200
      Re: Callable or not callable, that is the question! Ulrich Eckhardt <ulrich.eckhardt@dominolaser.com> - 2013-07-12 08:41 +0200
        Re: Callable or not callable, that is the question! Duncan Booth <duncan.booth@invalid.invalid> - 2013-07-12 07:36 +0000
          Re: Callable or not callable, that is the question! Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2013-07-12 09:49 +0000
            Re: Callable or not callable, that is the question! Peter Otten <__peter__@web.de> - 2013-07-12 12:36 +0200
    Re: Callable or not callable, that is the question! Jason Swails <jason.swails@gmail.com> - 2013-07-11 10:28 -0400
    Re: Callable or not callable, that is the question! Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2013-07-12 02:12 +0000
      Re: Callable or not callable, that is the question! Ian Kelly <ian.g.kelly@gmail.com> - 2013-07-12 01:11 -0600

#50441 — Callable or not callable, that is the question!

FromUlrich Eckhardt <ulrich.eckhardt@dominolaser.com>
Date2013-07-11 15:05 +0200
SubjectCallable or not callable, that is the question!
Message-ID<n6n2ba-ubg.ln1@satorlaser.homedns.org>
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


Bug or feature?


Thanks!

Uli

[toc] | [next] | [standalone]


#50446

FromPeter Otten <__peter__@web.de>
Date2013-07-11 16:11 +0200
Message-ID<mailman.4586.1373551911.3114.python-list@python.org>
In reply to#50441
Ulrich Eckhardt 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

Slightly modified example:

>>> @staticmethod
... def example(): return 42
... 
>>> class X:
...     example = example
... 
>>> X.example()
42
>>> example()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'staticmethod' object is not callable

When you access an attribute its __get__() method is implicitly called. This 
is part of the descriptor protocol:

>>> X.example
<function example at 0x7fe45c0ea3b0>
>>> example
<staticmethod object at 0x7fe45aafc090>
>>> example.__get__(X)
<function example at 0x7fe45c0ea3b0>

While it would be easy to make staticmethod callable

>>> class Staticmethod(staticmethod):
...     def __call__(self, *args, **kw):
...             return self.__func__(*args, **kw)
... 
>>> @Staticmethod
... def foo(): return "bar"
... 
>>> foo()
'bar'
>>> X.baz = foo
>>> X.baz()
'bar'

I see no clear advantage over the current situation.

> Bug or feature?

No bug. Missing feature if you come up with a convincing use-case.

[toc] | [prev] | [next] | [standalone]


#50501

FromUlrich Eckhardt <ulrich.eckhardt@dominolaser.com>
Date2013-07-12 08:41 +0200
Message-ID<11l4ba-u4l.ln1@satorlaser.homedns.org>
In reply to#50446
Am 11.07.2013 16:11, schrieb Peter Otten:
> Ulrich Eckhardt wrote:
>> Bug or feature?
>
> No bug. Missing feature if you come up with a convincing use-case.

class Parser:
     def _handle_bool(input):
         # ...
         pass

     types = {'bool': _handle_bool,
              'boolean': _handle_bool,}

     def parse(self, line):
         t,s,v = line.partition(':')
         handler = types[t]
         return handler(v)

I want a utility function that really behaves just like a function. I'd 
prefer to nest it inside the class that uses it, to make the code easier 
to understand. Since I don't want the implicit self-binding either, I 
would use staticmethod to make this clear, too.

Since I can live without any of this, it's not a big issue. What is to 
me a big issue though is the fact that Python behaves unexpectedly and 
reading Steven's response and the link there, it seems I'm not alone.

Greetings!

Uli


[toc] | [prev] | [next] | [standalone]


#50507

FromDuncan Booth <duncan.booth@invalid.invalid>
Date2013-07-12 07:36 +0000
Message-ID<XnsA1FB578CDBB3Cduncanbooth@127.0.0.1>
In reply to#50501
Ulrich Eckhardt <ulrich.eckhardt@dominolaser.com> wrote:

> Am 11.07.2013 16:11, schrieb Peter Otten:
>> Ulrich Eckhardt wrote:
>>> Bug or feature?
>>
>> No bug. Missing feature if you come up with a convincing use-case.
> 
> class Parser:
>      def _handle_bool(input):
>          # ...
>          pass
> 
>      types = {'bool': _handle_bool,
>               'boolean': _handle_bool,}
> 
>      def parse(self, line):
>          t,s,v = line.partition(':')
>          handler = types[t]
>          return handler(v)
> 
> I want a utility function that really behaves just like a function. I'd 
> prefer to nest it inside the class that uses it, to make the code easier 
> to understand. Since I don't want the implicit self-binding either, I 
> would use staticmethod to make this clear, too.

But the example you gave works just fine as written! You are only using 
your utility function as a function so there's no need for it to be a 
staticmethod or indeed any other sort of method.

To be a convincing use-case you would have to show a situation where 
something had to be both a static method and a utility method rather than 
just one or the other and also where you couldn't just have both.

If you can persuade me that you need _handle_bool as both a static method 
and a utility function, you probably also need to explain why you can't 
just use both:

class Parser:
    def _handle_bool(input): ...
    handle_bool = staticmethod(_handle_bool)


-- 
Duncan Booth http://kupuguy.blogspot.com

[toc] | [prev] | [next] | [standalone]


#50514

FromSteven D'Aprano <steve+comp.lang.python@pearwood.info>
Date2013-07-12 09:49 +0000
Message-ID<51dfd11a$0$9505$c3e8da3$5496439d@news.astraweb.com>
In reply to#50507
On Fri, 12 Jul 2013 07:36:30 +0000, Duncan Booth wrote:

> To be a convincing use-case you would have to show a situation where
> something had to be both a static method and a utility method rather
> than just one or the other and also where you couldn't just have both.

I have a class where I have a function that needs to be called both while 
the class is being constructed, and afterwards:

class Example:
    @staticmethod
    def do_stuff(arg):
        ...

    do_stuff(23)  # This doesn't work.

Example.do_stuff(42)


I have work-arounds: inside the class, I call do_stuff.__func__ instead 
of do_stuff, but really, that's ugly and distracting and merely a work-
around for the fact that staticmethods aren't callable. To make them 
callable is trivially easy: they just need a __call__ method that calls 
__func__ for you.



> If you can persuade me that you need _handle_bool as both a static
> method and a utility function, you probably also need to explain why you
> can't just use both:
> 
> class Parser:
>     def _handle_bool(input): ...
>     handle_bool = staticmethod(_handle_bool)

That's extremely inelegant. Why have two functions for something which is 
conceptually one?


-- 
Steven

[toc] | [prev] | [next] | [standalone]


#50516

FromPeter Otten <__peter__@web.de>
Date2013-07-12 12:36 +0200
Message-ID<mailman.4628.1373625429.3114.python-list@python.org>
In reply to#50514
Steven D'Aprano wrote:

> On Fri, 12 Jul 2013 07:36:30 +0000, Duncan Booth wrote:
> 
>> To be a convincing use-case you would have to show a situation where
>> something had to be both a static method and a utility method rather
>> than just one or the other and also where you couldn't just have both.
> 
> I have a class where I have a function that needs to be called both while
> the class is being constructed, and afterwards:
> 
> class Example:
>     @staticmethod
>     def do_stuff(arg):
>         ...
> 
>     do_stuff(23)  # This doesn't work.
> 
> Example.do_stuff(42)

That is a bit too abstract for my taste to count as a use-case. 

Also, as given the example will work in Python 3 when you remove the 
@staticmethod ;)

That said I can see that the error comes as a surprise and I would be fine 
with callable staticmethod objects.

[toc] | [prev] | [next] | [standalone]


#50448

FromJason Swails <jason.swails@gmail.com>
Date2013-07-11 10:28 -0400
Message-ID<mailman.4587.1373552924.3114.python-list@python.org>
In reply to#50441

[Multipart message — attachments visible in raw view] — view raw

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 "<stdin>", 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 "<stdin>", line 1, in <module>
TypeError: example() missing 1 required positional argument: 'self'
>>> inst.test = inst.example,
>>> inst.test[0]()
>>>

All the best,
Jason

[toc] | [prev] | [next] | [standalone]


#50484

FromSteven D'Aprano <steve+comp.lang.python@pearwood.info>
Date2013-07-12 02:12 +0000
Message-ID<51df65f7$0$9505$c3e8da3$5496439d@news.astraweb.com>
In reply to#50441
On Thu, 11 Jul 2013 15:05:59 +0200, Ulrich Eckhardt 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:
[...]
>      X.test2[0]() # TypeError: 'staticmethod' object is not callable
> 
> 
> Bug or feature?

In my opinion, a bug. I thought I had actually submitted it to the bug 
tracker, but apparently I was a shameful slacker and did not. However 
there was a discussion in this thread:

http://mail.python.org/pipermail/python-dev/2011-March/109090.html


Here's a simpler demonstration of the issue:

assert callable(staticmethod(lambda: None))


-- 
Steven

[toc] | [prev] | [next] | [standalone]


#50500

FromIan Kelly <ian.g.kelly@gmail.com>
Date2013-07-12 01:11 -0600
Message-ID<mailman.4616.1373613111.3114.python-list@python.org>
In reply to#50484
On Thu, Jul 11, 2013 at 8:12 PM, Steven D'Aprano
<steve+comp.lang.python@pearwood.info> wrote:
> On Thu, 11 Jul 2013 15:05:59 +0200, Ulrich Eckhardt 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:
> [...]
>>      X.test2[0]() # TypeError: 'staticmethod' object is not callable
>>
>>
>> Bug or feature?
>
> In my opinion, a bug. I thought I had actually submitted it to the bug
> tracker, but apparently I was a shameful slacker and did not. However
> there was a discussion in this thread:
>
> http://mail.python.org/pipermail/python-dev/2011-March/109090.html
>
>
> Here's a simpler demonstration of the issue:
>
> assert callable(staticmethod(lambda: None))

If staticmethod is going to be callable then classmethod should be
callable also.

[toc] | [prev] | [standalone]


Back to top | Article view | comp.lang.python


csiph-web