Python装饰的函数无法从另一个类更新属性 [英] Python Decorated Function Can't Update Attribute from Another Class

查看:63
本文介绍了Python装饰的函数无法从另一个类更新属性的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have an issue using a decorator from a imported package in a class I created. I created two classes and a main(). An instance of class A is created in main() and an instance of class B is created in class A. Class B needs to update attributes of the instance created in main(). Furthermore, I need to use decorators from the imported package.

我不知道如何解决无法从装饰的实例中装饰的 confirm_connect 函数引用 main()中创建的实例的属性的问题. B类.使它起作用的唯一方法是将在 main()中创建的实例声明为 global ,并删除 self 引用> B类.但是,将其设置为 global 会导致我的应用程序中出现其他问题.(将 socketio 的实例作为 B类中的 global 可以容忍,但我也不希望这样.)

I don’t know how to get around the inability to reference the attributes of the the instance created in main() from the decorated confirm_connect function in the instance of class B. The only way I have made it work is to declare the instance created in main() as global and remove all the self references in class B. However, making it global causes other issues in my application. (Making the instance of socketio as global within class B is tolerable, but I prefer not having it either.)

函数 confirm_connect 接收来自服务器的消息.如果将函数定义为 defconform_connect(self,data),则会收到错误消息 connect_confirm()缺少1个必需的位置参数:"data"/.如果我从声明中删除 self ,则会收到错误 NameError:名称'self'未定义.

The function confirm_connect receives a message from the server. If I define the function as def conform_connect(self, data), I get the error message connect_confirm() missing 1 required positional argument: 'data'/. If I remove self from the declaration, then I get the error NameError: name 'self' is not defined.

这是我的剧本.如何使我的脚本做我需要做的事?

Here is my script. How can I make my script do what I need it to do?

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

推荐答案

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

If you understand how decorators work, then you understand that

@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 参数,则不能满足方法的期望 )

And the reported problem is that sio.event expects a 1-argument, plain callback function that will receive the data; so with a self parameter it doesn't meet those expectations (and without a self parameter, the expectations of the method aren't met).

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

The insight is that (since 3.x; 2.x did things differently under the hood) a method defined in a class is just a function; it's the process of looking up that method from an instance that makes methods do the special things with self that they do.

因此,当您装饰方法时,最终将完全错误的内容注册为回调.无论您做什么, socketio.Client 都不了解您的B实例,也无法使用它.

So when you decorate the method, you end up registering totally the wrong thing as a callback. The socketio.Client doesn't know anything about your B instance and can't work with it, no matter what you do.

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

The solution is to instead use the bound instance method of your instance for the callback, which requires us to invoke the decorator manually as described at the beginning.

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

In the __init__, we can do something like:

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

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

And then we can define that method normally:

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

请注意,当我们执行装饰"时,现在如何在 __ init __ 上下文中编写 self.,所以我们告诉 socketio.Client connect_confirm 作为回调.我们不需要对装饰"标签做任何事情.结果,因此我们不会将其分配到任何地方.

Notice how in the __init__ context we can now write self. when we do the "decoration", so we tell the socketio.Client to use the connect_confirm of this instance as a callback. We don't need to do anything with the "decorated" result, so we don't assign it anywhere.

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

It's worth considering what this kind of thing looks like from the API's point of view. The socketio.Client class implementation presumably includes something like:

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

如果实现没有返回回调,那么很明显,您需要根据情况进行操作,因为装饰器语法将不可用.(好吧,您可以使用任何东西作为修饰符;但是在大多数情况下,恢复 None 或其他无效功能并不是很有用.)

If the implementation didn't return callback, it would be obvious what you needed to do in your situation, since the decorator syntax wouldn't have been available. (Well, you can use anything as a decorator; but getting back None or another non-function is not very useful most of the time.)

这篇关于Python装饰的函数无法从另一个类更新属性的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆