【问题标题】:How do I refer to a class method outside a function body in Python?如何在 Python 中引用函数体之外的类方法?
【发布时间】:2009-05-22 13:06:20
【问题描述】:

我想在 Observer 中进行一次回调注册。我不想在 init 或其他函数中进行注册。不知道有没有等价于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

【问题讨论】:

    标签: python


    【解决方案1】:

    我建议减少类的数量——记住 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")
    

    如果你想要一个 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 作为参数来订阅。

    【讨论】:

      【解决方案2】:

      当涉及到类定义中的函数式编程时,我已经开始接受 python 不是很直观。见this question。第一种方法的问题是 Observer 在构建类之前不作为命名空间存在。第二个问题是你创建了一个类方法,它在创建命名空间之前并没有真正做它应该做的事情。 (我不知道你为什么要尝试第三个。)在这两种情况下,这些事情都不会发生,直到 Observer 的类定义被填充之后。

      这听起来可能是一个可悲的约束,但实际上并没有那么糟糕。只需在类定义之后注册。一旦您意识到在模块主体中但在类主体之外的类上执行某些初始化例程并不是一种糟糕的风格,python 就会变得更加友好。尝试: 类观察者:

      # 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,您都可以保证此注册将执行一次且仅一次(除非进程分叉和其他一些不相关的边界情况)。

      【讨论】:

        【解决方案3】:

        哎呀。对于那个很抱歉。 我所要做的就是将订阅移到类定义之外

        class Observer:
        
                @classmethod
                def on_new_user_registration(new_user):
                    #body of handler...
        
        #after end of class
        
        NewUserRegistered().subscribe(Observer.on_new_user_registration)
        

        猜想这是太多 Java 的副作用,人们没有立即想到这一点。

        【讨论】:

        • 您使用的是什么版本的 Python?在 Python 2.5 中调用类中的类方法对我有用。
        • 这很奇怪。我使用的是 2.5.1,你下面的例子给了我 NameError
        • 这很可能是 2.5.1 中的一个错误。我想这足以阻止你使用我的语法。 :-)
        【解决方案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...
        

        【讨论】:

        • 但请注意selfcls 都是完全任意的——它们只是(非常强大的)约定。
        • 确实如此。我建议假装它们是语言强制的。几乎没有理由不这样做。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2014-03-07
        • 2016-08-03
        • 2017-01-11
        • 2017-03-26
        • 2023-04-09
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多