学习一下 streamlit!

4,148 阅读5分钟

自从在微信群中得知 streamlit,然后搜索了一下,现在微信一直给推送这个,所以花时间去做了一个 streamlit-g2 插件,通过写代码了解了一下它到底是什么?

Streamlit 是什么?

streamlit:A faster way to build and share data apps.

体验了之后,感觉对于大部分 python 处理算法后的页面呈现工作,已经不需要前端参入了!

streamlit 设计者真是一个天才,我们不少业务产品中的模块应该是可以做一些借鉴。它不仅仅是一个 python 框架,它包含了框架、插件、云服务,有非常好的开发体验。有点颠覆现在传统的前端做用户交互,后端脚本做数据服务的方式协作方式,直接基于 python 就能处理好数据、页面呈现、用户交互所有的事情,关键还非常的 pythonic。

直接引入 streamlit 的 Python 包,就可以利用 Python 的数据处理优势,结合 streamlit 内置的组件包,和社区生态插件去构建 UI。

例如,下面的代码运行出来,就直接是一个漂亮的页面。

import streamlit as st
from streamlit_g2 import g2

"""
# Welcome to [streamlit-g2](https://github.com/hustcc/streamlit-g2)!

[G2](https://github.com/antvis/G2) is a visualization grammar for dashboard building, data exploration and storytelling.

This project was created to allow us to render [G2](https://github.com/antvis/G2) charts in streamlit. In the meantime, below are some examples of what you can do with just a few lines of code:
"""

"""
## Bar Chart
"""

options = {
    "autoFit": True,
    "theme": "dark",
    "type": "interval",
    "data": [
        { "genre": "Sports", "sold": 275 },
        { "genre": "Strategy", "sold": 115 },
        { "genre": "Action", "sold": 120 },
        { "genre": "Shooter", "sold": 350 },
        { "genre": "Other", "sold": 150 },
    ],
    "encode": {
        "x": "genre",
        "y": "sold",
        "color": "genre",
    }
}

g2(options=options)

这已经满足了大量的 Python 从业者的前端需求,想到这里又焦虑了,前端的范围又小了。

streamlit-g2 插件开发

为了更加了解 streamlit,进去写代码才是最快的,看到 streamlit 的组件市场那么多插件,看了看可视化的非常多,毕竟数据处理完成下一步可视化分析了。

所以就想到为 G2 提供一个 streamlit 插件。没有找到 streamlit 官方提供的插件模板仓库,所以我直接看其他的组件怎么写的,然后 copy 过来调试和修改。

做完这一套就会觉得,streamlit 的架构师把这个插件的开发设计的很简洁明了,只要是做过前端,了解过一些 Python 的,基本都能发布一个质量不错的 streamlit 插件。

Python 代码

streamlit 生态插件的核心原理是利用 iframe 去绘制 UI,然后利用和 iframe 进行通信来驱动 UI 渲染,以及 UI 上的交互数据回传。

def your_api(a, b=None):
	_component_func = components.declare_component("your_name", path=frontend_dir)
	return _component_func(a=a, b=b)

插件的 Python 部分代码就是以上:

  • 暴露出去的一个 your_api的方法用来绘制这个 UI。
  • UI 的绘制就有 frontend 的代码去构建

官方提供了 React 的包,用来进行 iframe 和 parent 的通信,开发者仅仅需要关注与 API 参入到前端之后如何苏渲染即可。

前端代码

streamlit 提供了一个 streamlit-component-lib 的包,里面主要有:

  • withStreamlitConnection:对开发者自定义的 React 组件高阶装饰,才能进行互相的通信
  • ComponentProps:组件的 Props 定义,也是 Python 代码传过来的参入
  • Streamlit:提供一些 API 可以用来和外界通信,比如设置 iframe 高度、回传用户的交互状态等。

比如对于 streamlit-g2 来说,因为已经有对应的 G2 React 社区封装,所以实现起来非常简单。

import React from 'react';
import {
  Streamlit,
  withStreamlitConnection,
  ComponentProps,
} from 'streamlit-component-lib';
import { Chart } from '@berryv/g2-react';

const G2Component: React.FC<ComponentProps> = (props) => {
  const { style, options } = props.args

  return (
    <Chart
      style={{width: '100%', height: 400, ...style}} 
      options={options}
      onInit={() => Streamlit.setFrameHeight()}
    />
  )
};

export const G2 = withStreamlitConnection(G2Component)

以上做完就可以调试、发包了。

探讨点

本质是 Python 集合 JavaScript 做一些数据的交换,有点像我们在一个 native 中提供 JavaScript 容器做 UI 一样,只不过这个逻辑的颠倒过来的。

基于这样的前提,这里面就会存在一些问题可以探讨:

不同语言之间的数据结构不同

比如基础类型,Python 中的 True、False,日期类型 datetime 等对于 JavaScript 都有所不同,但是好在只是写法不同,他们之间的交换是能保留信息的。

方法怎么从 Python 传递到 JavaScript?

比如在 G2 中,我们需要使用 JavaScript 回调函数的方式,去自定义数据显示格式、颜色、大小逻辑等等。但是我们在 Python 中如何写一个 Function 是无法传递到 JavaScript 中的。

这里的解法就有两个:

  • 使用一个 string 描述 function 的逻辑,让所有 G2 配置都是纯 JSON 可以持久化存储的
  • 在 Python 中写 JavaScript,然后透传给前端去执行(eval、new Function 等)

方法一:对于简单的可以实现,比如格式化等,对于复杂的逻辑,其实就是再造一个 DSL 语言的解释器,那为啥不直接使用 JS 呢?

方法二:其实是利用 JS 语法作为这个传递的 DSL,然后前端浏览器自带这个 JS 解释器的,缺点就是需要 Pythoner 去写 JS 代码,体感不好。

性能究竟如何?

基本涉及到交互的 UI,都需要使用这种插件模型去研发,所以理论上一个稍微复杂的页面,会有大量的 iframe,这个页面性能理论上是不会太好,特别是信息密度很高的页面。

复杂布局页面无能为力

目前 streamlit 官方提供了单页面、多页面等布局方式,比较单一,但是如果是非常复杂、定制的页面,使用 streamlit 会非常复杂,甚至不可行。

这个点算是 streamlit 的缺点,也算是优势,毕竟 Python 的用户专注的还是数据处理和分析,前端展现上就应该有什么什么,屏蔽这里面的灵活性。

最后

广告时间,为了试试做一个 streamlit 插件,我都把 mac 系统升级到最新了,因为中间遇到一个 streamlit 在某个 macos 版本的问题,无法解决。

streamlit-g2,完成度比较高,可以直接应用,也提供了一些 DEMO 案例,来个 star ⭐ ⭐ ⭐️️️