
Polars 的 map_rows 默认将每行转为位置元组,但可通过预提取列名并构造字典,安全地向自定义函数传递具名列映射,避免“Already mutably borrowed”错误。
polars 的 `map_rows` 默认将每行转为位置元组,但可通过预提取列名并构造字典,安全地向自定义函数传递具名列映射,避免“already mutably borrowed”错误。
在 Polars 中,pl.DataFrame.map_rows 是一个逐行处理数据的高效方法,但它内部以位置元组(tuple)形式传递每行值,不携带列名信息。这导致直接在 lambda 中引用 df.columns(如 lambda x: udf(dict(zip(df.columns, x))))会触发 RuntimeError: Already mutably borrowed —— 因为 map_rows 执行期间 DataFrame 处于借用状态,而 df.columns 的访问尝试再次获取对同一对象的只读引用,在 Rust 层面违反了借用规则。
✅ 正确做法是:提前提取列名列表,脱离 DataFrame 生命周期约束,再在 map_rows 中复用:
import polars as pl
# 示例数据
df = pl.DataFrame({
"a": [1, 2, 3],
"b": ["x", "y", "z"],
"c": [True, False, True]
})
# 自定义函数:接收具名字典
def process_row(row_dict: dict) -> str:
return f"{row_dict['a']}_{row_dict['b'].upper()}_{str(row_dict['c']).lower()}"
# ✅ 安全写法:先提取 columns,再 map_rows
cols = df.columns # ← 关键:脱离 df 借用上下文
result = df.map_rows(lambda row_tuple: process_row(dict(zip(cols, row_tuple))))
# result 是一列 Series(默认名为 'map_rows'),类型为 pl.Utf8
print(result)
# shape: (3,)
# Series: 'map_rows' [str]
# [
# "1_X_true"
# "2_Y_false"
# "3_Z_true"
# ]⚠️ 注意事项:
-
map_rows返回的是单列Series(列名为'map_rows'),不会自动保留原始列结构或名称;若需多输出或多列结果,应改用pl.struct().map_elements()+ 表达式链(如df.with_columns(pl.struct(df.columns).map_elements(udf)))。 -
dict(zip(cols, row_tuple))依赖列顺序与row_tuple元素顺序严格一致(map_rows保证此一致性),无需额外校验。 - 性能提示:虽然语义清晰,但
map_rows+ 字典构造仍属 Python 层循环,对大数据集不如原生表达式(如pl.col("a").cast(pl.Utf8) + "_" + pl.col("b").str.to_uppercase())高效;仅在逻辑复杂、难以向量化时使用。
总结:Polars 的设计哲学是优先鼓励声明式、向量化操作;map_rows 是必要时的补充手段。通过“列名外提 + dict(zip())”模式,你可在不脱离 Polars API 的前提下,优雅获得具名参数体验,兼顾可读性与安全性。

















