如何在Python中在函数体外引用类方法?

1 投票
4 回答
2936 浏览
提问于 2025-04-15 11:46

我想在观察者(Observer)中进行一次性的回调注册。我不想在初始化(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

4 个回答

0

你现在做的应该是可以的:

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

不过有一点需要注意,类的方法用的是一个叫做 cls 的参数,而不是 self。所以,你的类定义应该像这样:

class Observer:

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

我建议减少类的数量——记住,Python和Java不一样。每次你使用 @classmethod@staticmethod 时,都应该停下来想一想,因为这两个关键词在Python中是比较少见的。

这样做是可行的:

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

如果你想要一个观察者类,可以这样写:

class Observer:

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

但要注意,静态方法无法访问协议实例,所以这可能不是很有用。你不能像这样订阅一个绑定到对象实例的方法,因为在类定义执行时,对象并不存在。

不过你当然可以这样做:

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)

在这里,我们使用绑定的 o.on_new_user_registration 作为订阅的参数。

1

我发现,Python在类定义中进行函数式编程时,确实不是特别直观。你可以看看这个问题。第一个方法的问题在于,Observer这个名字空间在类构建之前是不存在的。第二个方法的问题是,你创建了一个类方法,但在名字空间创建之前,它并不能正常工作。(我也不太明白你为什么要尝试第三个方法。)在这两种情况下,Observer的类定义完成之前,这些事情都不会发生。

这听起来可能是个令人沮丧的限制,但其实并没有那么糟糕。你只需要在类定义之后进行注册。一旦你意识到在模块的主体中,但在类的主体外执行某些初始化操作并不是坏风格,Python就会变得友好多了。试试这个:

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)

由于Python模块的工作方式,你可以确保这个注册操作只会执行一次,且只在Observer被导入的地方执行一次(当然,除了进程分叉和一些其他不相关的边界情况)。

撰写回答