惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

博客园_首页
GbyAI
GbyAI
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Apple Machine Learning Research
Apple Machine Learning Research
大猫的无限游戏
大猫的无限游戏
阮一峰的网络日志
阮一峰的网络日志
Last Week in AI
Last Week in AI
V
Visual Studio Blog
酷 壳 – CoolShell
酷 壳 – CoolShell
The Cloudflare Blog
博客园 - 【当耐特】
博客园 - 叶小钗
量子位
博客园 - 聂微东
S
SegmentFault 最新的问题
美团技术团队
Hugging Face - Blog
Hugging Face - Blog
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
月光博客
月光博客
宝玉的分享
宝玉的分享
小众软件
小众软件
罗磊的独立博客
有赞技术团队
有赞技术团队
Stack Overflow Blog
Stack Overflow Blog

博客园_首页

Plist 二进制格式 Milvus 和 PGVector,哪个更好? OpenClaw 已过时?在 VS Code 中运行 Hermes Agent! 第30篇文章:一个大三计科生的自白 Manim如何在数学公式中完美显示中文? Docker 部署 RocketMQ 5 并发编程核心概念辨析 C#事务处理最佳实践:别再让“主表存了、明细丢了”的破事发生 CLI 是什么?为什么大厂突然集体卷命令行? 【从0到1构建一个ClaudeAgent】协作-自主Agent UIImageView 设置图片不生效的原因排查 最小二乘问题详解20:无先验约束下的增量式SFM自由网平差 痞子衡嵌入式:大话双核i.MXRT1180之XIP应用里借助MU实现可靠Flash IAP的方法 AI Chat 封装, SemanticKerne.AiProvider.Unified 已发布 Windows下右键编辑js文件无法打开记事本——在注册表中使用环境变量 在后台服务中使用 Scoped 服务,为什么总是报错? H200 安装驱动并使用sglang启动模型 wireshark 抓包Trap上报告警内容 我用 AI 辅助开发了一系列小工具(2):图片压缩工具 [A Primer On MC and CC] 2.1 Memory Consistency 1 - 指令重排序和 SC 模型 Oracle数据库SCN推进技术详解与实践指南 玩转控件:封装个带图片的Label控件 Claude Code 4.7 真正该升级的不是模型,而是你的工作流 前端小白一句话,AI 帮我做了个颜值拉满的桌面媒体播放器。当代码不再是门槛,一句话编程就是现实。 5. WorkBuddy: 小龙虾的灵魂三件套,让你的小龙虾不只是工具 SQLite 分片方案实战:三种分片策略的深度对比 告别简陋 UI!一款基于 Fluent Design 和基于 WinUI 的开源免费、现代化的 Avalonia UI 控件库 关于二进制排列组合枚举的总结 AI开发-python-LangGraph框架(3-27-LangGraph从零实现大模型智能决策工作流) ElasticSearch主分片和副本分片概念详解
21. “|”不只是按位或,90%的人不知道
老陈说编程 · 2026-05-21 · via 博客园_首页

提到 Python 中的|,绝大多数开发者的第一反应都是按位或运算,5 | 3 = 7 是我们初学编程时就烂熟于心的基础。但很少有人知道,| 还能像这样用:

动画视频在《21. “|”不只是按位或,90%的人不知道》

chain = AddOne() | MulTwo() | ToStr()
print(chain.run(5))

Python 里的 | 不只是按位或,90% 的人不知道它还能这么用。一个符号就能把多个函数串成一条流水线,数据从左到右自动流动,代码像管道一样直观。这不是什么黑魔法,而是 Python 一个极其强大但被严重低估的特性 ——运算符重载。今天我们就来彻底解锁 | 运算符的隐藏技能,顺便揭秘 LangChain 中大名鼎鼎的 LCEL 表达式语言的核心原理。

运算符不是语法特权,只是语法糖

很多人觉得 +、-、*、| 这些运算符是 Python 内置的 "特殊语法",只能用于数字、字符串等基本类型。但实际上,Python 中所有运算符都对应着一个特殊的魔法方法。当你写 a | b 时,Python 解释器会自动将其转换为 a.__or__(b)。

这就是整个秘密的核心。只要我们在自己的类中定义了 __or__ 方法,就能完全自定义 | 运算符的行为。不仅是 |,理论上你可以重写所有运算符,让它们实现任何你想要的功能。

运算符 对应方法 默认含义
a & b a.__and__(b) 按位与
a | b a.__or__(b) 按位或
a ^ b a.__xor__(b) 按位异或
~a a.__invert__() 按位取反

用 | 打造你的数据流水线

要实现这样优雅的管道式调用,我们只需要定义几个简单的类。首先是所有任务的基类 Runnable,它规定了所有可运行任务必须实现 run 方法,同时重载了 __or__ 运算符,让两个任务可以用 | 连接成一个链。

class Runnable:
    def run(self, data):
        raise NotImplementedError
    
    def __or__(self, other):
        return Chain(self, other)

接下来是核心的 Chain 类,它本身也继承自 Runnable,这意味着一个任务链本身也是一个可运行的任务。Chain 会把所有步骤按顺序保存起来,执行时依次调用每个步骤的 run 方法,把前一步的输出作为后一步的输入。它也重载了 __or__ 方法,支持给已经存在的链继续追加新的步骤。

class Chain(Runnable):
    def __init__(self, *runnables):
        self.steps = list(runnables)
    
    def run(self, data):
        for step in self.steps:
            data = step.run(data)
        return data
    
    def __or__(self, other):
        return Chain(*self.steps, other)

有了这两个基类,我们就可以像搭积木一样定义各种单一职责的处理步骤了。每个步骤只需要继承 Runnable 并实现自己的 run 方法即可。

class AddOne(Runnable):
    def run(self, data):
        return data + 1
 
class MulTwo(Runnable):
    def run(self, data):
        return data * 2
 
class ToStr(Runnable):
    def run(self, data):
        return f"结果是: {data}"

现在,我们可以用最直观的方式把这些步骤串起来:

chain = AddOne() | MulTwo() | ToStr()
print(chain.run(5))  # 结果是: 12

| 让代码像管道一样直观,数据从左到右流动,一目了然。下次要讲的 LCEL 核心原理就是这个。