如何在Python中在函数体外引用类方法?
我想在观察者(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 个回答
你现在做的应该是可以的:
>>> 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...
我建议减少类的数量——记住,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
作为订阅的参数。
我发现,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被导入的地方执行一次(当然,除了进程分叉和一些其他不相关的边界情况)。