twisted xmlrpc 和 numpy float 64 异常
我正在使用numpy做一些事情,然后通过一个twisted/XMLRPC服务器来提供结果。如果结果是numpy的float 64类型,我会遇到一个异常,可能是因为twisted无法处理这种类型。实际上,如果我把结果降级为float32,像这样x=float(x),一切就正常了。这样做不是很好,因为如果我在某个地方忘记了这个解决办法,就会很麻烦。你有没有更好的解决方案?
服务器:
from twisted.web import xmlrpc, server
import numpy as np
class MioServer(xmlrpc.XMLRPC):
"""
An example object to be published.
"""
def xmlrpc_test_np(self):
return np.sqrt(2)
if __name__ == '__main__':
from twisted.internet import reactor
r = MioServer()
reactor.listenTCP(7080, server.Site(r))
reactor.run()
客户端:
import xmlrpclib
if __name__=='__main__':
x=xmlrpclib.ServerProxy('http://localhost:7080/')
print x.test_np()
异常:
Traceback (most recent call last):
File "C:\Users\Stone\.eclipse\org.eclipse.platform_4.3.0_1709980481_win32_win32_x86\plugins\org.python.pydev_2.8.2.2013090511\pysrc\pydevd.py", line 1446, in <module>
debugger.run(setup['file'], None, None)
File "C:\Users\Stone\.eclipse\org.eclipse.platform_4.3.0_1709980481_win32_win32_x86\plugins\org.python.pydev_2.8.2.2013090511\pysrc\pydevd.py", line 1092, in run
pydev_imports.execfile(file, globals, locals) #execute the script
File "C:\Users\Stone\Documents\FastDose\src\Beagle\Prove e test\xmlrpc_client.py", line 28, in <module>
print x.test_np()
File "C:\Python27\lib\xmlrpclib.py", line 1224, in __call__
return self.__send(self.__name, args)
File "C:\Python27\lib\xmlrpclib.py", line 1578, in __request
verbose=self.__verbose
File "C:\Python27\lib\xmlrpclib.py", line 1264, in request
return self.single_request(host, handler, request_body, verbose)
File "C:\Python27\lib\xmlrpclib.py", line 1297, in single_request
return self.parse_response(response)
File "C:\Python27\lib\xmlrpclib.py", line 1473, in parse_response
return u.close()
File "C:\Python27\lib\xmlrpclib.py", line 793, in close
raise Fault(**self._stack[0])
xmlrpclib.Fault: <Fault 8002: "Can't serialize output: cannot marshal <type 'numpy.float64'> objects">
3 个回答
我尝试了很长时间用xmlrpc传输一张640x480x3的图片。之前提到的“tolist()”和“flatten”这两种方法都没能成功。最后我找到了这个解决方案:
发送端:
def getCamPic():
cam = cv2.VideoCapture(0)
img = cam.read()
transferImg = img.tostring()
return transferImg
接收端:
transferImg = proxy.getCamPic()
rawImg = np.fromstring(transferImg.data,dtype=np.uint8)
img = np.reshape(rawImg,(640,480,3))
因为我知道我的摄像头分辨率,所以硬编码对我来说没问题。一个改进的版本可能会把这些信息包含在transferImg里?
我找到了一种方法:在发送结果之前,我先把它传给函数 to_float()
。
def iterable(x):
try:
iter(x)
except: # not iterable
return False
else: # iterable
return True
def to_float(x):
from numpy import float64,ndarray
if type(x) == dict:
res = dict()
for name in iter(x):
res[name] = to_float(x[name])
return res
elif type(x) == ndarray:
return map(float, x.tolist())
elif type(x) == float64:
return float(x)
elif iterable(x) and not isinstance(x,str):
res=[]
for item in x:
if type(item) == float64:
res.append(float(item))
elif type(x) == ndarray:
res.append(map(float, x.tolist()))
else:
res.append(item)
return res
else:
return x
这和twisted没有关系。如果你仔细看你发的错误信息,最后会看到错误出现在xmlrpclib.py文件里。
xml-rpc的实现使用了marshal来对对象进行序列化。不过,xml-rpc的序列化方式不支持处理第三方对象,比如numpy.ndarray
。当你把它转换成float
时之所以能正常工作,是因为内置的float类型是被支持的。
在我给出解决方案之前,我想指出,这个问题在其他地方也被问过很多次,你可以通过谷歌轻松找到相关信息(1 2),我也是从那里借鉴的答案。
要实现你想要的功能,你可以把numpy数组转换成可以序列化的东西。最简单的方法是写一些扁平化和反扁平化的函数。然后在发送数据时调用扁平化函数,在接收数据时调用反扁平化函数。这里有个例子(来自这篇帖子):
from cStringIO import StringIO
from numpy.lib import format
def unflatten(s):
f = StringIO(s)
arr = format.read_array(f)
return arr
def flatten(arr):
f = StringIO()
format.write_array(f, arr)
s = f.getvalue()
return s
更简单的方法是在发送时调用
<the array you want to send>.tolist()
把数据转换成python列表,然后在接收时调用
np.array(<the list you received>)
来恢复数据。
这样做的唯一缺点是你在发送和接收数据时必须明确调用扁平化和反扁平化函数。虽然这多了一些输入,但并不算太多,如果你忘记了,程序会出错(会出现你之前遇到的同样错误),而不是默默地做错事。
我从你的问题中了解到,你不太喜欢这样,想找一种方法让numpy数组能直接和xml-rpc一起工作,而不需要显式的扁平化和反扁平化。我觉得这可能不太可行,因为xml-rpc的文档明确说明,唯一可以被序列化的第三方对象是带有__dict__
属性的新式类,并且在这种情况下,键必须是字符串,值必须是其他兼容的类型。
所以你看,如果想直接支持numpy数组,似乎必须修改xml-rpc的序列化方式。如果能在ndarray
的子类中添加某种方法来支持序列化,那就好了,但看起来并不是这样。
希望这能帮助你理解发生了什么,以及为什么现有的解决方案需要显式的扁平化和反扁平化。
-Daniel
附言:这让我对如何扩展python中的xml-rpc以支持其他类型的对象产生了好奇,所以我也发了我自己的问题。