简体   繁体   中英

How can I implement pattern event to call classmethod in Python?

I want to call some function depend on event code how can I do it in Python?

I made such code but it not works and I am user that only one step to make it working.

class Thing(object):
    @classmethod
    def do1(cls):
        print 'do1'

    @classmethod
    def do2(cls):
        print 'do2'

    eventToMethod = {'1': do1,
                     '2': do2}

    @classmethod
    def onEvent(cls, name):
        method = cls.eventToMethod.get(name)
        if method != None:
            method()

Thing.onEvent('1')

Whatever I get such errors and has not idea how to call classmethods in Python way.

TypeError: 'classmethod' object is not callable

Can you help with this simple problem?

You need to make some changes to changes to eventToMethod first, don't assign do1 , do2 to it, better assign strings. You can always access class attributes using strings. The problem with storing references to do1 and do2 in dictionary is that they are not bound methods yet(they're simply classmethod objects(non-data descriptors)) when you store them in dictionary, it's only after the completion of class definition that they're converted to fully bound class methods.

eventToMethod = {'1': 'do1',
                 '2': 'do2'}

And then use getaattr to get the method:

@classmethod
def onEvent(cls, name):
    method = getattr(cls, cls.eventToMethod.get(name))
    ...

Note that you can also directly pass 'do1' to onEvent instead of keeping a dictionary to store names and then simply use:

method = getattr(cls, name)

You can still get away with your current approach if you call __get__ method of do1 , do2 descriptors explicitly.:

method = cls.eventToMethod.get(name)
if method != None:
    method.__get__(None, cls)()

This works because this is exactly what Python does under the hood, classmethod is a non-data descriptor and when you do Thing.do1 Python actually calls __get__ method of do1 with first agument as None and second as type:

>>> Thing.do1.__get__(None, Thing)
<bound method type.do1 of <class '__main__.Thing'>>
>>> Thing.do1.__get__(None, Thing)
<bound method type.do1 of <class '__main__.Thing'>>
>>> Thing.do1
<bound method type.do1 of <class '__main__.Thing'>>
>>> Thing.eventToMethod['1'].__get__(None, Thing)   #Using OP's code.
<bound method type.do1 of <class '__main__.Thing'>>

While I understand that this doesn't answer your question directly, I thought it might be useful to see an alternative.

Often it's possible to use reflection to calculate the correct method at runtime. For example:

    @classmethod
    def onEvent(cls, name):
        try:
            method = getattr(cls, 'do%s'%name)
        except AttributeError:
            return

        method()

This approach may be useful if you are able to follow a strict naming convention in your methods (as in the example, where you seem to prefix them with 'do'). It's similar to how PyUnit detects the set of test cases to run.

This avoids the need to maintain a dict, which may get out of sync with the actual methods on the object. It also arguably leads to clearer code.

It's worth pointing out as well that if you are attempting to do some kine of event-driven programming -- There are libraries/frameworks that help facilitate this:

Example:

#!/usr/bin/env python

from circuits import Component, Event


class Thing(Component):

    def do1(self):
        print("do1")

    def do2(self):
        print("do2")

    def started(self, manager):
        self.fire(Event.create("do1"))
        self.fire(Event.create("do2"))
        raise SystemExit(0)


Thing().run()

Output:

$ python foo.py
do1
do2

Disclaimer: I'm the author of circuits

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM