问题描述
我在我创建的类中使用来自导入包的装饰器时遇到问题。我创建了两个类和一个 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
或其他非函数在大多数情况下不是很有用。)