微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

Python 装饰函数无法从另一个类更新属性

如何解决Python 装饰函数无法从另一个类更新属性

我在我创建的类中使用来自导入包的装饰器时遇到问题。我创建了两个类和一个 main()class A 的实例在 main() 中创建,class B 的实例在 class A 中创建。 Class B 需要更新在 main() 中创建的实例的属性。此外,我需要使用导入包中的装饰器。

我不知道如何解决无法从 main() 实例中的装饰 confirm_connect 函数引用 class B 中创建的实例的属性的问题。我让它工作的唯一方法是将在 main() 中创建的实例声明为 global删除 self 中的所有 class B 引用。但是,将其设为 global 会导致我的应用程序出现其他问题。 (将 socketio 的实例设置为 global 内的 class B 是可以容忍的,但我也不喜欢这样做。)

函数 confirm_connect 从服务器接收消息。如果我将函数定义为 def conform_connect(self,data),则会收到错误消息 connect_confirm() missing 1 required positional argument: 'data'/。如果我从声明中删除 self,则会收到错误 NameError: name 'self' is not defined

这是我的脚本。我怎样才能让我的脚本做我需要它做的事情?

import socketio

class A():

    def __init__(self):
        self.pin = None
        
    def connect_to_server(self):
        self.io = B()
        self.io.connect_to_server()

    def start_the_process(self):
        self.b = B(self)
        self.b.connect_to_server()
        
    def do_something_with_pin(self):
        print(self.pin)

class B():
    
    global sio
    sio = socketio.Client()
    
    def __init__(self,a):
        self.a = a
        
    def connect_to_server(self):    
        sio.connect('http://my_url_is_this.org')    
        sio.emit('manual_connection_parameter',{'foo': 'bar'})
            
    @sio.event
    def connect_confirm(data):
        self.a.pin = data
        self.a.do_something_with_pin()
        
def main():
    a = A()
    a.start_the_process()

if __name__ == '__main__':
    main()

解决方法

如果您了解装饰器的工作原理,那么您就会明白

@sio.event
def connect_confirm(self,data):
    self.a.pin = data
    self.a.do_something_with_pin()

只是用于

的语法糖
def connect_confirm(self,data):
    self.a.pin = data
    self.a.do_something_with_pin()
connect_confirm = sio.event(connect_confirm)

报告的问题是 sio.event 需要一个 1 个参数的普通回调函数,它将接收 data;因此,使用 self 参数时,它不符合这些期望(如果没有 self 参数,则无法满足方法的期望)。

洞察力是(因为 3.x;2.x 在幕后做了不同的事情)在类中定义的方法只是一个函数;这是从实例中查找该方法的过程,这使得方法可以使用 self 来完成它们所做的特殊事情。

因此,当您装饰该方法时,您最终将完全错误的东西注册为回调。 socketio.Client 对您的 B 实例一无所知,也无法使用它,无论您做什么。

解决方案是使用实例的绑定实例方法进行回调,这需要我们按照开头所述手动调用装饰器。

__init__ 中,我们可以执行以下操作:

def __init__(self,a):
    self.a = a
    sio.event(self.connect_confirm)

然后我们可以正常定义该方法:

def connect_confirm(self,data):
    self.a.pin = data
    self.a.do_something_with_pin()

注意在 __init__ 上下文中,我们现在可以在进行“装饰”时编写 self.,因此我们告诉 socketio.Client 使用 connect_confirm 作为回调。我们不需要对“装饰”的结果做任何事情,所以我们不会将它分配到任何地方。


从 API 的角度来看,这种事情是值得考虑的。 socketio.Client 类实现大概包括以下内容:

class Client:
    # ... lots of other stuff...
    def event(self,callback):
        self._event_callback = callback
        return callback
    def _some_internal_logic(self):
        if _special_situation_occurs():
            self._event_callback(self._get_data_for_callback())

如果实现没有 return callback,很明显您需要在您的情况下做什么,因为装饰器语法不可用。 (好吧,您可以使用任何东西作为装饰器;但是返回 None 或其他非函数在大多数情况下不是很有用。)

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。