How can I refer to a class method outside of a function body in Python?

I want to do a one time callback registration in Observer. I don't want to do registration inside init or other function. I don't know if there is a level equivalent for init

    class Observer:

        @classmethod
        def on_new_user_registration(new_user):
            #body of handler...

        # first I try

        NewUserRegistered().subscribe \
          (Observer.on_new_user_registration) #gives NameError for Observer

        #so I try

        NewUserRegistered().subscribe(on_new_user_registration) #says not callable

        #neither does this work

        NewUserRegistered().subscribe(__metaclass__.on_new_user_registration)


class BaseEvent(object):
    _subscriptions = {}

    def __init__(self, event_info = None):
        self.info = event_info

    def fire(self):
        for callback in self._subscriptions[event_type]:
            callback(event_info)

    def subscribe(self, callback):
        if not callable(callback):
            raise Exception(str(callback) + 'is not callable')
        existing = self._subscriptions.get(self.__class__, None)
        if not existing:
            existing = set()
            self._subscriptions[self.__class__] = existing
        existing.add(callback)

    class NewUserRegistered(BaseEvent):
        pass

      

+1


a source to share


4 answers


I've come to the conclusion that python is not very intuitive when it comes to functional programming in class definitions. See this question . The problem with the first method is that Observer doesn't exist as a namespace until the class is built. The problem with the second is that you've created a class method that doesn't actually do what it should, until the namespace is created. (I have no idea why you are trying to do the third one). In both cases, none of these things happen until the Observer class definition is complete.

This may sound like a sad limitation, but it really isn't that bad. Just register after defining the class. Once you realize that it is not bad style to do certain initialization routines for classes in the body of a module, but outside of the body of the class, python becomes much friendlier. Try: Observer class:



# Define the other classes first

class Observer:
    @classmethod
    def on_new_user_registration(new_user):
        #body of handler...
NewUserRegistered().subscribe(Observer.on_new_user_registration)

      

Because of the way modules work in python, you are guaranteed that this registration will be done once and only once (no handling and possibly some other irrelevant edge cases) wherever the Observer is imported.

+1


a source


I suggest reducing the number of classes - remember that Python is not Java. Every time you use @classmethod

or @staticmethod

, you should stop and think about it, as these keywords are quite rare in Python.

Doing it like this:

class BaseEvent(object):
    def __init__(self, event_info=None):
        self._subscriptions = set()
        self.info = event_info

    def fire(self, data):
        for callback in self._subscriptions:
            callback(self.info, data)

    def subscribe(self, callback):
        if not callable(callback):
            raise ValueError("%r is not callable" % callback)
        self._subscriptions.add(callback)
        return callback

new_user = BaseEvent()

@new_user.subscribe
def on_new_user_registration(info, username):
    print "new user: %s" % username

new_user.fire("Martin")

      

If you want the Observer class, you can do it like this:

Observer class:



@staticmethod
@new_user.subscribe
def on_new_user_registration(info, username):
    print "new user: %s" % username

      

But note that the static method does not have access to the protocol instance, so this is probably not very useful. You cannot subscribe to a method associated with an object instance like this, as the object will not exist when the class definition is executed.

But you can of course do this:

class Observer:
    def on_new_user_registration(self, info, username):
        print "new user: %s" % username

o = Observer()
new_user.subscribe(o.on_new_user_registration)

      

where we use bound o.on_new_user_registration

as an argument to subscribe.

+2


a source


oops. Sorry about that. All I had to do was move the subscription outside of the class definition

class Observer:

        @classmethod
        def on_new_user_registration(new_user):
            #body of handler...

#after end of class

NewUserRegistered().subscribe(Observer.on_new_user_registration)

      

Guess this is a side effect of too much Java that you don't immediately think about.

0


a source


What you are doing should work:

>>> class foo:
...     @classmethod
...     def func(cls):
...             print 'func called!'
...
>>> foo.func()
func called!
>>> class foo:
...     @classmethod
...     def func(cls):
...             print 'func called!'
...     foo.func()
...
func called!

      

It should be noted, however, that class methods take a cls argument instead of a self argument. So your class definition should look like this:

class Observer:

    @classmethod
    def on_new_user_registration(cls, new_user):
        #body of handler...

      

0


a source







All Articles