How to use a decorator in class level correctly?
I have a message bus, and a class subscribes many methods to the message bus, for example:
class BookingService(object):
def start(self):
self.msg_bus.login(self.user, self.password)
self.msg_bus.subscribe('/broadcast/aliveResponse', self.handleAliveResponse)
self.msg_bus.subscribe('/b开发者_如何学编程roadcast/musicInfoUpdated', self.handleMusicInfo)
self.msg_bus.subscribe('/broadcast/radioOnline', self.handleRadioOnline)
self.msg_bus.subscribe('/broadcast/radioOffline', self.handleRadioOffline)
self.msg_bus.subscribe('/broadcast/online', self.handleBroadcastOnline)
self.msg_bus.subscribe('/proxy/aliveResponse', self.handleEvent)
self.msg_bus.subscribe('/proxy/online', self.handleProxyOnline)
self.msg_bus.subscribe('/proxy/radioReady', self.handleEvent)
self.msg_bus.subscribe('/proxy/radioUpdate', self.handleEvent)
self.msg_bus.subscribe('/proxy/radioClosed', self.handleEvent)
self.msg_bus.subscribe('/message_bus/detached', self.handleDetached)
self.msg_bus.run()
It works, but it is difficult to understand what is the message path for each method, what I want is to use a decorator to subscribe to the message bus with method, it would looks like this
class BookingService(object):
@subscribe('/broadcast/aliveResponse')
@subscribe('/broadcast/onLine')
def handleEvent(self, dest, data):
print dest, data
@subscribe('/proxy/aliveResponse')
def handleAnotherEvent(self, dest, data):
print dest, data
But here are some difficulty to solve, first of all, the msg_bus attribute belongs to instance, namely, to self. I can't get the self.msg_bus in class level. To solve this problem, I can write it like this:
class BookingService(object):
subscribations = []
def subscribe(dest):
"""Decorator for subscribing function to destination
"""
def callee(func):
subscribations.append((dest, func))
return func
return callee
def subscribe_all(self):
for dest, func in self.subscribations:
self.msg_bus.subscribe(dest, func)
@subscribe('/broadcast/aliveResponse')
def handleEvent(self, dest, data):
print dest, data
def start(self):
self.subscribe_all()
I try to collect subscriptions into BookingService.subscribations and add them to msg_bus later in subscribe_all, but here comes the problem. I got an error
subscribations.append((dest, func))
NameError: global name 'subscribations' is not defined
It seems that the subscribations is not in the scope of subscribe function, how to solve this problem?
Rather than putting it into a list on the instance, put it into a list on the function or class.
class BookingService(object):
def subscribe(dest):
"""Decorator for subscribing function to destination
"""
def callee(func):
if not hasattr(func, 'subscriptions'):
func.subscriptions = []
func.subscriptions.append((dest, func))
return func
return callee
def subscribe_all(self):
for classmember in dir(self):
for dest, func in getattr(getattr(self, classmember), 'subscriptions', []):
self.msg_bus.subscribe(dest, func)
@subscribe('/broadcast/aliveResponse')
def handleEvent(self, dest, data):
print dest, data
def start(self):
self.subscribe_all()
You may still have issues with methods not being bound, however; take care with what "self" means.
You may also want to move "subscribe" outside the class definition, or rename it "_subscribe" or make it cope with being called as self.subscribe()
(at the moment self.subscribe()
will work but not how you expect)
You define subscribations in the class body, which makes it a class attribute. However, when you use it, you use it as a global. You would have to get it from the method, but since this happens before the class has been created, you can't.
You can set it as an attribute on the function, or you can have a global dictionary with a mapping between functions and destinations. In both cases you will have to go through all functions in the class in the subscribe_all method to see if they have subscriptions.
精彩评论