简体   繁体   中英

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

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

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.

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.

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.

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...

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