第8章 处理多个输入与输出
随着应用功能越来越丰富,往往需要不止一个输入。
计算器需要两个数字,文本生成应用可能同时要提示词、风格、长度和语言。
机器学习应用可能需要一张图片和一个置信度阈值,文档分析应用则可能需要一个文件加一个问题。
只要理解了组件与函数之间如何传递值,Gradio 就能自然地应对这些场景。
多个函数参数
从一个 Python 函数开始:
def calculate_rectangle(length, width):
area = length * width
perimeter = 2 * (length + width)
return area, perimeter
两个输入,两个输出。
直接这样表示:
import gradio as gr
def calculate_rectangle(length, width):
area = length * width
perimeter = 2 * (length + width)
return area, perimeter
with gr.Blocks() as demo:
length = gr.Number(label="Length")
width = gr.Number(label="Width")
area = gr.Number(label="Area")
perimeter = gr.Number(label="Perimeter")
button = gr.Button("Calculate")
button.click(
fn=calculate_rectangle,
inputs=[length, width],
outputs=[area, perimeter]
)
demo.launch()
对应关系很直观:
length → first function parameter
width → second function parameter
输出同理:
area → first returned value
perimeter → second returned value
顺序的重要性
假设你的函数是:
def calculate(length, width):
...
而你又写了:
inputs=[width, length]
函数会按你列出的顺序接收值,不会自动纠正。
Gradio 并不关心你本意是让第一个组件叫 "length",它只是按配置好的顺序传递。
所以给变量起个清晰的名称很重要。
不同类型的多个输入
组件不必是同一种类型。
比如:
def generate_profile(name, age, interests):
return (
f"{name} is {age} years old. "
f"Their interests include: {', '.join(interests)}."
)
可以这样定义组件:
name = gr.Textbox()
age = gr.Number()
interests = gr.CheckboxGroup(
choices=["AI", "Web Development", "Design", "Data Science"]
)
然后:
button.click(
fn=generate_profile,
inputs=[name, age, interests],
outputs=output
)
这是实际应用中非常常见的模式。
返回不同类型
一个函数可以返回不同类型的数据。
例如:
def analyze_number(number):
doubled = number * 2
description = f"The number {number} was doubled."
return doubled, description
然后:
number_output = gr.Number()
text_output = gr.Textbox()
再写:
button.click(
fn=analyze_number,
inputs=number,
outputs=[number_output, text_output]
)
第一个输出是数值,第二个是文本。
返回结构化信息
假设你要分析一个人的信息:
def analyze_person(name, age):
category = "adult" if age >= 18 else "minor"
return {
"name": name,
"age": age,
"category": category
}
可以使用:
result = gr.JSON()
当应用包含多个相关字段时,这种方式很有用。
返回表格
假设用户上传了信息,而你的 Python 函数会生成一个表格:
def generate_scores():
return [
["Alice", 95],
["Bob", 88],
["Charlie", 91]
]
然后:
table = gr.Dataframe(
headers=["Student", "Score"]
)
函数返回的数据就能填充到表格中。
输出不必同时显示
有时你的应用会有不同的模式。
例如,下拉框可以列出多个选项供用户选择:
Summary
Detailed Analysis
Raw Data
应用会根据用户的选择更新对应的输出。
动态组件的行为在此发挥了作用。
可选值与空输入
真实用户不会每次都填完所有字段。
假设:
def create_greeting(first_name, last_name):
return f"Hello, {first_name} {last_name}!"
如果 last_name 为空,输出结果可能会显得不自然。
可以这样处理:
def create_greeting(first_name, last_name):
first_name = first_name.strip()
last_name = last_name.strip()
if last_name:
return f"Hello, {first_name} {last_name}!"
return f"Hello, {first_name}!"
这也提醒我们:界面设计与 Python 校验是配合工作的。
设计表单
下面来创建一个小应用,用于收集一本书的相关信息。
import gradio as gr
def create_book_summary(title, author, genre, rating):
return (
f"Title: {title}\n"
f"Author: {author}\n"
f"Genre: {genre}\n"
f"Rating: {rating}/10"
)
with gr.Blocks() as demo:
title = gr.Textbox(label="Book Title")
author = gr.Textbox(label="Author")
genre = gr.Dropdown(
choices=[
"Fiction",
"Science Fiction",
"Fantasy",
"Mystery",
"Non-fiction"
],
label="Genre"
)
rating = gr.Slider(
minimum=1,
maximum=10,
value=5,
step=1,
label="Rating"
)
submit = gr.Button("Create Summary")
output = gr.Textbox(
label="Book Summary",
lines=6
)
submit.click(
fn=create_book_summary,
inputs=[title, author, genre, rating],
outputs=output
)
demo.launch()
注意每种输入组件承担不同的功能。
分组相关输入
表单越长,越不希望界面变成一长列到底。后面我们会用行、列、分组和标签页来组织组件。
目前你只需要理解一个核心概念:多输入本质上就是传给事件的组件列表。
一次操作产生多个输出
以一个图像分析应用为例。
它可能输出:
预测类别,
置信度评分,
描述文本,
以及处理后的图像。
对应的 Python 函数可以返回四个值:
def analyze_image(image):
label = "cat"
confidence = 0.94
description = "The image appears to contain a cat."
processed = image
return label, confidence, description, processed
界面可以包含:
label = gr.Textbox()
confidence = gr.Number()
description = gr.Textbox()
processed = gr.Image()
然后:
button.click(
fn=analyze_image,
inputs=image,
outputs=[
label,
confidence,
description,
processed
]
)
这样,用户只需一次操作,整个结果区域就会同步更新。
返回 None
有时函数并不需要更新所有输出。
在合适的场景下,你可以对某个输出返回 None,根据你设计的交互行为,保留其原值或将其清空。
例如:
def process(value):
if not value:
return "Please enter a value.", None
return "Success", value
设计多输出函数时,要明确每个返回值的具体含义。
在 Interface 中使用多输入
同样的思路也适用于 gr.Interface。
例如:
import gradio as gr
def calculate(a, b):
return a + b, a * b
demo = gr.Interface(
fn=calculate,
inputs=[
gr.Number(label="First Number"),
gr.Number(label="Second Number")
],
outputs=[
gr.Number(label="Sum"),
gr.Number(label="Product")
]
)
demo.launch()
因此,Interface 同样支持多路输入和输出。
什么时候从 Interface 迁移到 Blocks
如果你只需要:
inputs → function → outputs
那用 Interface 就够了。
但如果需要:
多个按钮
自定义事件关系
复杂布局
动态更新
标签页
状态管理
多个独立的工作流
那 Blocks 通常能给你更强的控制力。
一个更贴近实际的例子
我们来搭一个简单的 AI 写作配置界面。
用户输入主题、语气、篇幅、是否包含示例,以及语言。
import gradio as gr
def generate_article(
topic,
tone,
length,
include_examples,
language
):
examples = "Include practical examples." if include_examples else "Do not include examples."
return (
f"Topic: {topic}\n"
f"Tone: {tone}\n"
f"Length: {length}\n"
f"Language: {language}\n"
f"{examples}"
)
with gr.Blocks() as demo:
topic = gr.Textbox(
label="Topic",
lines=4
)
tone = gr.Dropdown(
choices=["Professional", "Friendly", "Academic", "Casual"],
label="Tone"
)
length = gr.Slider(
minimum=100,
maximum=5000,
value=1000,
step=100,
label="Approximate Length"
)
include_examples = gr.Checkbox(
label="Include practical examples"
)
language = gr.Dropdown(
choices=["English", "Spanish", "French", "German"],
label="Language"
)
button = gr.Button("Generate")
output = gr.Textbox(
label="Configuration"
)
button.click(
fn=generate_article,
inputs=[
topic,
tone,
length,
include_examples,
language
],
outputs=output
)
demo.launch()
这个例子还没有真正生成文章,但这是有意为之。
我们先掌握界面的搭建模式,理解之后,再把函数换成真正的 AI 模型就容易多了。
避免写巨型函数
当应用有十个输入时,很容易就想写一个巨大的函数,把所有逻辑全塞进去。
但这并不总是好选择。
更好的做法是把职责拆分开:
def validate_inputs(...):
...
def build_prompt(...):
...
def call_model(...):
...
def format_result(...):
...
再用一个轻量的编排函数把各步串起来:
def generate(...):
validate_inputs(...)
prompt = build_prompt(...)
result = call_model(prompt)
return format_result(result)
这样 Gradio 事件处理器就清晰可控了。
动手试试
搭建一个"旅行规划器"界面。
让用户输入:
目的地
天数
预算
旅行风格
兴趣偏好
返回至少三个输出:
简短的行程摘要
每日预算估算
推荐活动
暂时不用调用 AI 模型,用普通 Python 逻辑实现即可。
重点在于练习管理多个输入和输出。
要点总结
函数可以接收多个输入。
事件可以把多个组件连到同一个函数上。
函数可以返回多个输出。
输出的顺序必须与返回值顺序一致。
输入可以是完全不同的组件类型。
结构化结果可以用
JSON或Dataframe等组件展示。复杂应用将界面代码与业务逻辑分离,更易维护。