异步装饰器返回NoneType错误求助:树莓派Socket客户端代码修正
解决你的Socket客户端异步装饰器NoneType错误问题
看起来你在异步装饰器的实现上踩了几个常见的坑,我帮你一步步定位并修正问题:
核心错误原因:装饰器未返回包装函数
你的connection方法定义了内部的wrapper1,但没有返回这个函数,所以当你调用client.connection(("192.168.0.11", 56500))时,方法默认返回None,这就导致了@None的错误——你试图用None作为装饰器,自然会抛出'NoneType' object is not callable。
除了这个核心问题,还有几个异步逻辑的问题需要修正,下面是完整的修正代码和解释:
修正后的Client类代码
import socket, asyncio class Client: def __init__(self): self.s = None self.data = None self.addr = None # 提前初始化addr,避免close时可能的错误 def connection(self, addr): """An asynchronous decorator for any server mainloop. Just decorate it with this and add a Client parameter.""" def wrapper1(func): async def wrapper2(*args): await self.connect(addr) # connect是async方法,需要await await func(self) await self.close_conn() # close_conn也是async方法,需要await # 移除重复的func调用,避免执行两次业务逻辑 return wrapper2 # 关键:返回wrapper1,让装饰器能正常工作 return wrapper1 async def connect(self, addr): """Connects to the desired server. You can make a server mainloop with the connection decorator.""" self.s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.s.connect(addr) self.addr = addr print(f"Connected to {addr}") async def close_conn(self): """Closes connection with the address connected to.""" if self.s: self.s.close() print(f"Disconnected from {self.addr}") self.addr = None # 重置而不是del,避免后续属性不存在的问题 async def send_bytes(self, bytes_data): """Sends bytedata through a pipe to connected address""" if self.s: self.s.sendall(bytes_data) print(f"Sent {bytes_data.decode()} in bytedata format to {self.addr}") async def recv(self, buf_size): """Recieves data from a server in bytedata format""" if self.s: self.data = self.s.recv(buf_size) return self.data # 返回数据,方便调用者直接使用
修正后的main.py代码
import asyncserver import asyncio # 需要导入asyncio来运行异步函数 client = asyncserver.Client() @client.connection(("192.168.0.11", 56500)) async def mainloop(cli): # 必须定义为async函数,因为装饰器里会await它 await cli.send_bytes(b'Hello!!') # 异步方法需要await received_data = await cli.recv(1024) # 接收数据并获取返回值 if received_data: print(received_data.decode()) # 运行异步主函数 asyncio.run(mainloop())
关键修改点说明
- 装饰器返回包装函数:在
connection方法末尾添加了return wrapper1,这是装饰器能正常工作的核心。 - 异步方法调用加await:
connect、close_conn、send_bytes、recv都是异步方法,调用时必须加await才能实际执行。 - 修复重复执行逻辑:移除了
wrapper2中重复的return await func(self),避免你的业务逻辑被执行两次。 - mainloop改为async函数:因为装饰器内部会用
await调用它,所以必须定义为异步函数。 - 添加异步运行入口:用
asyncio.run(mainloop())来启动你的异步主逻辑,否则异步函数不会被执行。 - 增加空值检查:在
close_conn、send_bytes、recv中添加了self.s的空值检查,避免连接异常时的报错。 - recv返回数据:让
recv返回接收到的数据,方便调用者直接使用,同时保留self.data的赋值。
现在你可以运行修正后的代码,应该能正常连接服务器并收发数据了。
内容的提问来源于stack exchange,提问作者codernaut1
相关产品推荐
相关产品推荐

