[Tutor] Problem with calling class methods stored in a list

Steven D'Aprano steve at pearwood.info
Thu Jan 10 13:11:46 CET 2013


On 10/01/13 21:13, Tobias Marquardt wrote:
> Hello,
>
> I have a class with some class methods that are also stored in a list.
> Now I have a problem calling these methods.

Peter Otten has already given one solution. Read on for two more.

> Essentially the relevant code looks like this:
>
> class MyClass(object):
>
>     @classmethod
>     def foo(cls):
>          cls.method_list[0]()
>
>     @classmethod
>     def bar(cls):
>         print("Hello World")
>
>     method_list = [bar]
>
>
> So foo() takes bar() from the list and tries calls it, which results in
> an error:
>
> File "aco/antsystem/test.py", line 11, in foo
> cls.method_list[0]()
> TypeError: 'classmethod' object is not callable
>
> I guess the reason is that bar() needs to be called on the class like:
> cls.bar(), but how to I achieve this here?

To answer your direct question, all you need to do is manually imitate what
Python does when you call a descriptor (see below):


py> MyClass.method_list[0].__get__(MyClass, None)()
Hello World


So, inside the foo() method, do this:

     @classmethod
     def foo(cls):
         cls.method_list[0].__get__(cls, None)()


and it should work.

"What the hell is a descriptor?" I hear you ask. Don't worry about them,
they are an advanced part of Python, used to implement methods (regular,
class and static) and properties. If you care, you can read this:

http://docs.python.org/2/howto/descriptor.html

but you may find it heavy going. (I know I did the first five times I
read it.)

Here's another solution: use a regular function object instead of a
method. This relies on an unusual, but deliberate, quirk of the way Python
implements methods: they are actually regular functions inside the class
body when the class is created, and don't get turned into methods until you
do a lookup like "self.spam" or "cls.ham".


class MyClass(object):
     @classmethod
     def foo(cls):
         # Note that you manually provide the class argument.
         cls.method_list[0](cls)

     def bar(cls):
         print("Hello World")

     # At this time, bar is still a regular function object, not a
     # method of any sort.
     method_list = [bar]
     del bar  # just to avoid any confusion


By *not* turning bar into a class method, it remains an ordinary
function object. You then store the function object inside the list,
and when you access it via method_list[0] Python gives you the regular
function, not a method. Since it is a regular function, it is callable,
but you have to manually provide the cls argument.



-- 
Steven



More information about the Tutor mailing list