tags:

views:

243

answers:

4

I want to do a one time callback registration within Observer. I don't want to do the registration inside init or other function. I don't know if there is a class 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: 

I've come to accept that python isn't very intuitive when it comes to functional programming within class definitions. See this question. The problem with the first method is that Observer doesn't exist as a namespace until the class has been built. The problem with the second is that you've made a class method that doesn't really do what it's supposed to until after the namespace has been created. (I have no idea why you're trying the third.) In both case neither of these things occurs until after the class definition of Observer has been populated.

This might sound like a sad constraint, but it's really not so bad. Just register after the class definition. Once you realize that it's not bad style to perform certain initialization routines on classes in the body of the module but outside the body of the class, python becomes a lot friendlier. Try: class Observer:

# 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 performed once and only once (barring process forking and maybe some other irrelevant boundary cases) wherever Observer is imported.

David Berger
A: 

oops. sorry about that. All I had to do was to move the subscription outside 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 it is a side-effect of too much Java that one doesn't immediately think of this.

bagheera
What version of Python are you using? Calling the class method inside the class worked for me in Python 2.5.
Jason Baker
that is strange. I am using 2.5.1 and your example below gives me NameError
bagheera
It may very well be a bug in 2.5.1. I suppose that's enough to preclude you from using my syntax. :-)
Jason Baker
A: 

What you're 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!

One thing to note though, class methods take a cls argument instead of a self argument. Thus, your class definition should look like this:

class Observer:

    @classmethod
    def on_new_user_registration(cls, new_user):
        #body of handler...
Jason Baker
yeah. thanks for that correction about cls
bagheera
But do note that both `self` and `cls` are completely arbitrary -- they are just (very strong) conventions.
Martin Geisler
Indeed. I'd recommend pretending they're language-enforced though. There's very little reason not to do so.
Jason Baker
+1  A: 

I suggest to cut down on the number of classes -- remember that Python isn't Java. Every time you use @classmethod or @staticmethod you should stop and think about it since these keywords are quite rare in Python.

Doing it like this works:

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 an Observer class, then you can do it like this:

class Observer:

@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 can not subscribe a method bound to an object instance like this since the object wont 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 the bound o.on_new_user_registration as argument to subscribe.

Martin Geisler