【问题标题】:How to use a decorator in class level correctly?如何正确使用类级别的装饰器?
【发布时间】:2010-12-08 02:34:28
【问题描述】:

我有一个消息总线,一个类为消息总线订阅了很多方法,例如:

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('/broadcast/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()

它可以工作,但是很难理解每个方法的消息路径是什么,我想要的是使用装饰器来订阅带有方法的消息总线,它看起来像这样

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

但是这里要解决一些难点,首先msg_bus属性属于instance,即属于self。我无法获得班级级别的 self.msg_bus。为了解决这个问题,我可以这样写:

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()

我尝试将订阅收集到 BookingService.subscribations 并稍后在 subscribe_all 中将它们添加到 msg_bus,但问题来了。我遇到了错误

subscribations.append((dest, func))
NameError: global name 'subscribations' is not defined

好像订阅不在订阅功能的范围内,请问如何解决?

【问题讨论】:

  • 要最巧妙地做到这一点,您需要一个元类(或者可能是一个超类)。

标签: python class decorator


【解决方案1】:

与其将其放入实例的列表中,不如将其放入函数或类的列表中。

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()

但是,您可能仍然会遇到方法未绑定的问题;注意“自我”的含义。

您可能还想将“订阅”移到类定义之外,或将其重命名为“_subscribe”或使其能够应对被称为self.subscribe() 的情况(目前self.subscribe() 可以工作,但不是你所期望的)

【讨论】:

    【解决方案2】:

    您在类主体中定义订阅,这使其成为类属性。 但是,当您使用它时,您将其用作全局。你必须从方法中得到它, 但由于这是在创建类之前发生的,所以你不能。

    您可以将其设置为函数的属性,或者您可以拥有一个全局字典,其中包含函数和目标之间的映射。在这两种情况下,您都必须通过 subscribe_all 方法中的类中的所有函数来查看它们是否有订阅。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-12-15
      • 2019-11-17
      • 2019-02-20
      • 2012-08-07
      • 2014-06-08
      • 2021-11-18
      • 1970-01-01
      相关资源
      最近更新 更多