当前位置: 首页 > 面试题库 >

用Flask提供Matplotlib图像的成语之间有什么区别?

仲孙华奥
2023-03-14
问题内容

网络搜索变成了几个简单的(无证)的例子(和良好的答案在这里约)如何动态服务使用瓶Matplotlib数字; 但是这些功能的特点以及它们之间的差异使我感到困惑。

一些使用低级IO并返回元组

io = StringIO.StringIO()
plt.savefig(io, format='png')
io.seek(0)
data = io.read()
return data, 200, {'Content-type': 'image/png'}

而其他几个 使用不同的IO API并返回一个Response

io = StringIO.StringIO()
canvas = FigureCanvas(fig)
canvas.print_png(io)
response = make_response(io.getvalue())
response.mimetype = 'image/png' # or response.headers['Content-Type'] = 'image/png'
return response

而其他人则采用不同的方法来编码和构建返回值

io = StringIO.StringIO()
fig.savefig(io, format='png')
data = io.getvalue().encode('base64')
return html.format(data)

所有这些似乎都有效。但是我想知道它们是否共享这些方法的功能,或者它们之间的差异具有非显而易见的后果(例如,对于性能或对不同场景的适用性)。

第一,

扮演了什么角色StringIO; 这是准备服务(任何形式)图像的唯一方法吗?
在我的Python生活中,我从未见过使用过它,也不清楚为什么它似乎是服务器(二进制?)文件处理的必需部分。

其次,我想知道这些示例采用不同的方法来打包其响应。特别

使用seekplus read,vs getvalue.或具有相同的作用是否有意义?
是什么决定了返回方法的选择:元组vs. html.formatvs. Response(带有make_response);最后
为什么有些方法Content-type显式地设置显式,而另一些方法将编码(设置为“ base64”)?
这些方法中的任何一种是否被视为“最佳”或最新的惯用(或至少Pythonic)方法?


问题答案:

StringIO扮演什么角色;这是准备服务(任何形式的)图像的唯一方法吗?

首先,不,这不是唯一的方法。“经典”方式将涉及文件系统:

让matplotlib创建一个绘图。
将相应的图像数据持久保存到文件系统中的文件中(这涉及到上下文切换到调用诸如之类的系统调用的内核write())。
再次读取该文件的内容(这使内核可以通过来为您读出文件系统read())。
通过定义良好的数据编码以及正确设置的标头的HTTP响应,将内容提供给客户端。
步骤(3)和(4)涉及文件系统交互。也就是说,内核实际上与硬件组件进行通信。这需要时间(对于传统的hrad驱动器,由于访问时间长,因此仅向光盘写入几个字节可能需要几毫秒的时间)。现在,问题是:您是否需要将图像数据持久化到磁盘上?如果答案为“否”,则可以通过将图像数据保留在Web应用程序进程的内存中,从而跳过与文件系统的整个交互过程并节省一些时间。这对以下StringIO方面有好处:

StringIO是Python中非常通用的工具,可提供类似文件的对象,而实际数据永远不会委托给内核以将其写入文件系统或从文件系统读取。它保存在内存中。这就是为什么StringIO对象也称为内存中文件的原因。

问题的关键是,plt.savefig() 希望有一个对象作为第一个参数,看起来像,实际上代表在文件系统中实际文件的对象。StringIO提供了这样的对象,但是-在后台-将数据写入当前进程堆中的缓冲区,并在需要时再次从那里读取数据。

通过读取/写入一小部分数据StringIO需要花费纳秒或微秒的时间,而与文件系统的交互通常要慢几个数量级。

现在,请不要误会我的意思:通常,文件系统足够快,并且操作系统具有自己的技术来使文件系统交互尽可能快。如前所述,真正的问题是:您是否需要持久保存图像数据?如果以后不关心访问此图像数据,则不要涉及文件系统。这就是您显示的三个摘要的创建者所决定的。

出于性能原因,用StringIO替换实际文件系统交互可能是一个非常非常有效的决定。但是,您的Web应用程序中肯定还有其他瓶颈。例如,使用StringIO可以将请求响应延迟减少5毫秒。但是,考虑到网络延迟为100 ms,这实际上是否重要?另外,请记住,发送大型文件内容最好不要打扰一个严肃的Web应用程序-使用完善的Web服务器(也可以利用sendfile()系统调用)可以更好地满足这些要求。在这种情况下,让matplotlib将文件写入文件系统,然后告诉您的Web服务器(通过X-Sendfile标头)来完成剩下的工作。因此,性能是一个复杂的主题,可能不是最有力的论据。但是只有您知道您的要求!

使用搜索加读取与获取值之间是否有任何意义,或者这些操作实际上具有相同的作用

本质上是一样的。没有概念上的差异,也没有(重大的)性能差异。

是什么决定了返回方法的选择:元组vs. html.format vs. Response(带有make_response);最后

没有明确的答案。有很多方法可以将数据获取到客户端。没有“正确”的方法,只有更好或更糟。最好采用哪种方法在很大程度上取决于Web框架。使用Flask,make_response()是创建响应对象的规范方法。html.format()可能有一些我不知道的优点-您需要自己了解一下!但是,请继续阅读,我认为Flask中内置了一种完全适合您的情况的方法。

为什么有些方法显式设置Content-type,而另一些方法设置编码(为“ base64”)?

有通过HTTP将文件发送到浏览器的正确和不正确的方法。通常,HTTP响应应包含某些标头(另请参阅所需的HTTP响应标头)。仅出于您的理解,您可能需要阅读这些详细信息。当然,二进制数据需要使用客户端可以理解的编码进行编码,并且必须在响应标头中阐明该编码。另外,正确的HTTP响应应包含MIME类型(内容类型)。您所介绍的方法似乎并不能真正控制另一种方法(没有冒犯性,简单易懂的例子通常更着重于一件事而不是另一件事)。

我认为您确实应该使用Flask的send_file方法来为您处理一些重要的事情。此方法有两个参数。我会通过明确定义MIME类型mimetype。第一个参数可以是类似文件的对象,因此StringIO对象可以正常工作。但是,在这种情况下,您需要执行以下操作seek(0):

在调用send_file()之前,请确保文件指针位于要发送的数据的开头。

以下两种方法在语义上是优雅的(我认为),应适当注意对文件内容进行编码和设置HTTP响应标头:

from flask import send_file 

1)

f = StringIO.StringIO()
plt.savefig(f, format='png', dpi=300)
f.seek(0)
send_file(f, mimetype='image/png')

2)

plt.savefig('image.png', dpi=300)
send_file('image.png', mimetype='image/png')

在第二种情况下,如果正确配置,您的Web服务器(例如nginx)可以为您传输文件。



 类似资料:
  • OSGi和JavaSPI有什么区别?利弊是什么?

  • 问题内容: 在此示例中: 无法编译为: 而被编译器接受。 这个答案说明唯一的区别是,与不同,它允许您稍后引用类型,似乎并非如此。 是什么区别,并在这种情况下,为什么不第一编译? 问题答案: 通过使用以下签名定义方法: 并像这样调用它: 在jls§8.1.2中,我们发现(有趣的部分被我加粗了): 通用类声明定义了一组参数化类型(第4.5节), 每种可能通过类型arguments调用类型参数节的类型

  • 问题内容: 使用Docker时,我们从基础映像开始。我们启动它,创建更改,并将这些更改保存在形成另一个映像的层中。 因此,最终我为自己的PostgreSQL实例提供了一个映像,为我的Web应用程序提供了一个映像,对它们的更改将继续保留。 什么是容器? 问题答案: 图像的实例称为容器。您有一张图像,该图像是您描述的一组图层。如果启动此映像,则该映像具有正在运行的容器。您可以有多个运行中的同一图像容器

  • Docker和虚拟机的映像有什么区别吗?除了图像格式,我在任何地方都找不到任何信息。请评论像图像大小,实例创建时间,捕获时间等。谢谢!

  • 问题内容: 我知道什么是循环(对键进行迭代),但是第一次听说(对值进行迭代)。 我对循环感到困惑。我没有形容词。这是下面的代码: 我得到的是,遍历属性值。那么,为什么它不记录(返回)而不是?但是循环遍历每个键()。在这里,循环还会遍历键。但是不会迭代财产的价值,即。为什么会这样呢? 总而言之: 在这里,我控制台循环。它应该记录,但是在这里记录。为什么呢 问题答案: 遍历对象的可枚举属性名称。 (E

  • 问题内容: 和之间有什么区别 例如,当你查看类时,构造函数具有以下签名: 对于方法之一: 问题答案: 第一个说是“是E的祖先的某种类型”。第二个说是“某种类型,它是E的子类”。(在两种情况下,本身都可以。) 因此,构造函数使用该? 形式,以确保在从集合中获取值时,它们将全部为E或某个子类(即,它是兼容的)。该方法试图将值放入集合中,因此集合必须具有 或超类的元素类型。 例如,假设你有一个这样的类层