术语表

此页面是 SymPy 文档中使用的各种术语的术语表。此术语表主要针对 SymPy 特定的术语。有关更通用的 Python 术语,请参阅 Python 术语表。数学术语仅在 SymPy 中具有特定含义时才包含在此处。有关一般的数学定义,请参考其他来源,例如 维基百科MathWorld,以及特定 SymPy 函数文档中的参考文献。

反导数

函数 \(f(x)\) 关于 \(x\) 的**反导函数**是指一个函数 \(F(x)\),满足 \(\frac{d}{dx}F(x) = f(x).\) 它有时也被称为 \(f(x)\) 的“不定积分”,并写成 \(\int f(x)\,dx.\) 在 SymPy 中,可以使用 integrate() 计算反导函数。请注意,有些资料将它称为 \(f(x)\) 的“原函数”,但 SymPy 中不使用这个术语,因为它不像“反导函数”那样普遍使用,而且“原函数”在数学和 SymPy 中还有其他含义。

args

SymPy 表达式args 属性是一个元组,包含用于创建表达式的顶层 子表达式。它们是用于创建表达式的类的参数。任何表达式的 args 都可以通过 .args 属性获得。例如,(1 + x*y).args(1, x*y),因为它等于 Add(1, x*y)argsfunc 共同完全定义了一个表达式。始终可以通过重复使用 .args 来遍历 表达式树 并提取 SymPy 表达式的任何子表达式。每个 SymPy 表达式都可以使用 funcargs 准确地重建,也就是说,expr.func(*expr.args) == expr 对于任何 SymPy 表达式 expr 始终为真。表达式的 args 可以为空元组 (),这意味着表达式是一个 原子

假设

假设是关于 符号表达式 的一组谓词,它们定义了符号或表达式可以取值的集合。一些假设示例包括 positiverealinteger。假设在逻辑上彼此相关,例如,integer 的假设会自动意味着 real。假设使用 三值逻辑 系统,其中谓词为 TrueFalseNone

假设要么被假设,要么被查询。例如,符号 x 可以被假设为正数,方法是将其定义为 x = symbols('x', positive=True)。然后,可以在包含此符号的表达式上查询假设,例如 (x + 1).is_real,在这种情况下,它将返回 True

如果在符号上没有假设任何假设,则默认情况下符号被假设为一般复数。设置假设很重要,因为某些简化仅在受限域中是数学上正确的,例如,\(\sqrt{x^2} = x\) 对于一般的复数 \(x\) 不成立,但对于 \(x\) 为正数时成立。SymPy 函数永远不会在表达式上执行任何操作,除非该操作对于其假设允许的所有值都成立。

SymPy 具有两个独立的假设系统,它们彼此密切相关。第一个系统有时被称为“旧假设”,因为它比较旧,在这个系统中,假设是在 Symbol 对象上假设的,并使用 is_* 属性进行查询。第二个系统有时被称为“新假设”,在这个系统中,假设使用单独的谓词对象(例如 Q.positive)进行假设,并使用 ask() 函数进行查询。新的假设系统能够支持更复杂的查询,但它的开发程度也不如旧的假设系统。目前,大多数 SymPy 用户应该优先使用旧的假设系统。

有关假设的更多详细信息,请参阅 假设指南

原子

原子是指 args 为空元组 () 的表达式。原子是 表达式树 的叶子。例如,如果一个函数使用递归来遍历使用 args 的表达式树,那么原子表达式将是递归的基线情况。

请注意,Atom 类有时被用作原子表达式的基类,但原子表达式继承此类并不是必须的。表达式成为原子的唯一要求是它的 args 为空。

自动简化

自动简化指的是在类构造函数内部自动发生的任何简化。例如,x + xAdd 构造函数中会自动简化为 2*x。与手动 简化 不同,自动简化只能通过设置 evaluate=False 来禁用(请参阅 Unevaluated)。自动简化通常是为了使表达式变得 规范化。不建议过度使用自动简化,因为它会使在不使用诸如 evaluate=False 之类的技巧的情况下表示表达式的非简化形式变得不可能,而且在类构造函数中执行自动简化通常会很昂贵。相反,通常更倾向于使用手动 简化/规范化

Basic

Basic 是所有 SymPy 表达式的超类。它定义了 SymPy 表达式所需的基本方法,例如 argsfunc相等性不可变性,以及一些有用的表达式操作函数,例如 替换。大多数 SymPy 类将继承更具体的 Basic 子类,例如 BooleanExprFunctionMatrix。不是 Basic 实例的对象通常不能在 SymPy 函数中使用,除非它可以通过 sympify() 转换为 Basic 实例。

Boolean

Booleanlogic 模块中类的基类。 Boolean 实例表示作为 布尔代数 中元素的逻辑谓词,可以认为它们具有“真”或“假”值(请注意,Boolean 对象不使用 三值逻辑,而 假设 使用三值逻辑)。

绑定符号

表达式中的符号如果它不是自由的,则称该符号为绑定。绑定符号可以在任何地方替换为新符号,而不会改变表达式的数学等价性。绑定符号的例子包括定积分中的积分变量和Subs中的替换变量。绑定符号有时用虚拟符号表示,但并不总是Dummy对象,而Dummy对象并不总是绑定符号。

规范形式
规范化

表达式通常可以用多种数学上等价的方式表示。规范形式是表示表达式的唯一方式,所有等价表达式都可以转换为这种方式。一个被转换为规范形式的表达式被称为规范化。规范形式通常是唯一的,并且具有使其更容易处理的特性。例如,有理函数常用的规范形式是\(\frac{p}{q}\),其中\(p\)\(q\)是无公因子的展开多项式。

代码生成

代码生成是指将 SymPy 表达式转换为某种语言或库的代码以便其可以被数值评估的过程。SymPy 支持生成数十种语言和库的代码,包括 C、C++、Fortran 和 NumPy。

核心

核心是包含所有 SymPy 对象使用的重要功能的子模块。这包括BasicExpr基类,AddMulPow等类,以及假设.

虚拟

虚拟符号是指一个自动与任何其他虚拟符号(除了它自己)不相等的符号,即使它们有相同的名称。当一个函数需要返回一个包含新符号的表达式时,使用虚拟符号,这样它就不会意外地与具有相同名称的符号发生冲突。虚拟符号可以使用Dummy创建。

等式

等式是指包含等号\(=\)表达式。SymPy 中的等式使用Eq类表示。等式不是使用==运算符创建的。 ==运算符对两个表达式进行结构相等性检查,并且总是返回TrueFalse。与之形成对比的是,符号等式可能是未评估的。等式被认为是布尔值,因为它们在数学上表示一个谓词值,该值要么为真,要么为假。

_eval_*

可以使用特殊的_eval_*方法在子类上定义BasicExpr上的各种方法。例如,一个对象可以通过定义一个_eval_derivative方法来定义它将如何被diff()函数处理。使用_eval_*方法而不是覆盖方法本身,这样基类上定义的方法可以在分派到_eval_*方法之前进行预处理。

evalf

evalf是每个Expr对象上存在的方法,它将表达式评估为浮点数值,或者如果表达式包含符号,则将表达式的常量部分转换为数值。 .n()方法和N()函数都是evalf的简写。“evalf”代表“evaluate floating-point”。evalf在内部使用mpmath将表达式评估到任意精度。

评估

评估可以指

Expr

Expr是所有代数 SymPy 表达式的超类。它本身是Basic的子类。可以在AddMulPow中的 SymPy 表达式应该是Expr子类。并非所有 SymPy 类都是Expr的子类,例如,布尔值对象是Basic,但不是Expr,因为布尔表达式在AddMul等类中没有数学意义。

表达式

任何 SymPy 对象,即Basic的任何实例,都可以称为表达式。有时,“表达式”一词保留给Expr对象,它们是代数表达式。表达式不要与等式混淆,等式是表示数学等式的特定类型的表达式。

表达式树

表达式树 是一个由 结构组成的 表达式。 每个表达式都由更小的表达式构建成树。 表达式树的节点是表达式,每个节点的子节点是构成该表达式的直接 子表达式。 或者,可以将表达式树视为一个树,其中非叶节点是 函数,而叶节点是 原子。 在 教程 中展示了一个示例表达式树。 任何 SymPy 表达式的表达式树可以通过递归遍历 参数 来获得。 注意,由于 SymPy 表达式是 不可变的,并且严格通过 结构相等 来判断是否相等,因此也可以将表达式树视为一个 DAG,其中相同的子表达式在图中只表示一次。

自由符号

表达式中的 符号 如果表达式在数学上依赖于该符号的值,则该符号是 自由的。 也就是说,如果用一个新符号替换该符号,结果将是一个不同的表达式。 不自由的符号是 绑定 的。 表达式的自由符号可以通过 free_symbols 属性访问。

func

func 属性是 表达式 的函数,可以通过 expr.func 获得。 这通常与 type(expr) 相同,但在某些情况下可能不同,因此在使用 参数 重新构建表达式时,应该优先使用 expr.func 而不是 type(expr)。 每个 SymPy 表达式都可以使用 funcargs 准确地重建,也就是说,expr.func(*expr.args) == expr 对于任何 SymPy 表达式 expr 始终为真。

函数

函数 可能指

  • 数学函数,即一个将值从定义域映射到值域的函数。 有时,包含 符号表达式 被口语化地称为“函数”,因为符号可以使用 替换 来替换为一个值,从而 计算 该表达式。 这种用法是口语化的,因为必须使用 subs 方法来执行此操作,而不是典型的 Python 函数调用语法,而且因为它没有明确说明表达式是哪个变量的函数,所以通常应该优先使用术语“表达式”,除非是真正的函数。 可以使用 Lambda 将表达式转换为可以使用 Python f(x) 语法调用的函数对象。

  • SymPy Function 类的实例。

  • Python 函数,即使用 def 关键字定义的函数。 Python 函数不是 符号的,因为它们必须始终返回一个值,因此不能 不进行计算

Function (类)

Function 是 SymPy 中符号函数的基类。 这包括常见的函数,如 sin()exp(),特殊函数,如 zeta()hyper(),以及积分函数,如 primepi()divisor_sigma()。 函数类始终是 符号的,这意味着它们通常在传递 符号 时会 不进行计算,例如 f(x)。 不是每个符号 表达式 类都是 Function 子类,例如,AddMul 这样的 核心 类不是 Function 子类。

Function 也可以用于通过传递一个函数的字符串名称来创建一个 未定义的函数,例如 Function('f')

并非 SymPy 中的每个函数都是符号 Function 类;有些只是始终返回值的 Python 函数。 例如,大多数简化函数,如 simplify(),无法用符号表示。

不可变

在 Python 中,如果对象不能在原地修改,则该对象是 不可变的。 为了改变一个不可变对象,必须创建一个新的对象。 在 SymPy 中,所有 Basic 对象都是不可变的。 这意味着所有对 表达式 进行操作的函数都将返回一个新表达式,而不会改变原始表达式。 对表达式进行操作永远不会改变引用该表达式的其他对象或表达式。 这也意味着,任何两个 相等 的对象都是完全可互换的,可以认为是同一个对象,即使它们恰好是内存中的两个不同对象。 不可变性使得更容易维护代码的心理模型,因为没有隐藏状态。 SymPy 对象是不可变的,这也意味着它们是可散列的,这使得它们可以用作字典键。

交互式

交互式 用法是指在交互式 REPL 环境(如 Python 提示符、isympyIPythonJupyter notebook)中使用 SymPy。 当交互式使用 SymPy 时,所有命令都是由用户实时输入的,所有中间结果都会显示出来。 交互式 用法与 编程式 用法相反,编程式用法是指将代码写入一个文件,该文件要么作为脚本执行,要么作为更大的 Python 库的一部分。 有些 SymPy 惯用法只建议在交互式使用时使用,并且在编程式使用时被认为是反模式。 例如,在交互式使用 SymPy 时,运行 from sympy import * 很方便,但在编程式使用中通常不被推荐,在编程式使用中,显式导入名称只使用 import sympy 是首选。

is_*

SymPy 中以is_ 开头的属性,并使用小写名称,会查询该对象上给定的假设(注意:有一些属性是这个规则的例外,因为它们没有使用假设系统,请参见假设指南)。例如,x.is_integer 会查询x 上的integer 假设。使用大写名称的is_* 属性会测试一个对象是否为给定类的实例。有时,同一个名称会同时存在小写和大写属性,但它们指的是不同的东西。例如,x.is_Integer 只有在xInteger 的实例时才为True,而x.is_integerx 在假设系统中为integer 时为True,例如 x = symbols('x', integer=True)。一般来说,建议不要使用is_Capitalized 属性。它们存在于历史原因,但它们是不必要的,因为可以用isinstance() 实现相同的功能。另请参见Number

isympy

isympy 是 SymPy 附带的一个命令,它会在命令行上启动一个交互式 会话,其中所有 SymPy 名称都被导入,并且打印 被启用。它默认使用IPython(如果安装了)。

Kind

SymPy 对象的kind 表示它代表的数学对象类型。对象的 kind 可以通过kind 属性访问。示例 kind 包括NumberKind(表示复数)、MatrixKind(表示其他 kind 的矩阵)以及BooleanKind(表示布尔谓词)。SymPy 对象的 kind 与它的 Python 类型不同,因为有时单个 Python 类型可以表示多种不同类型的对象。例如,Matrix 可以是复数矩阵,也可以是来自其他环的值对象的矩阵。有关 SymPy 中 kind 的更多详细信息,请参见SymPy 对象分类 页面。

lamda

Lamda” 只是希腊字母“lambda”的另一种拼写方式。它有时在 SymPy 中使用,因为lambda 是 Python 中的保留关键字,所以代表 λ 的符号必须用其他名称命名。

lambdify()

lambdify() 是一个将 SymPy 表达式转换为可数值计算的 Python 函数的函数,通常会使用数值 库,如 NumPy。

Matrix

Matrix 指的是 SymPy 用于表示矩阵的类集。SymPy 有几个内部类来表示矩阵,具体取决于矩阵是符号的(MatrixExpr)还是显式的,可变的还是不可变的,密集的还是稀疏的,以及底层元素的类型,但这些通常都称为“Matrix”。

mpmath

mpmath 是一个用于任意精度数值的纯 Python 库。它是 SymPy 的硬依赖。mpmath 能够计算数值 函数到任意给定的位数。每当 SymPy 以数值方式评估表达式时,例如使用evalf 时,mpmath 都会在幕后使用。

Numeric

数值 表示或算法是对数值输入直接进行操作的表示或算法。它与符号 表示或算法形成对比,后者可以在未评估的形式下处理对象。数值算法通常与符号算法有很大不同。例如,数值求解微分方程通常意味着使用龙格-库塔 等算法评估微分方程,以在给定初始条件的情况下找到一组数值点,而符号求解微分方程(例如使用 SymPy 的dsolve())意味着用数学方法对微分方程进行操作,以生成一个符号 方程,该方程表示解。符号微分方程解可能包括可以表示任何数值的符号常数。数值算法通常围绕着浮点数带来的问题而设计,例如精度损失和数值稳定性,而符号算法并不关心这些问题,因为它们可以精确地计算事物。

除了 SymPy 之外的大多数科学库,如 NumPy 或 SciPy,都是严格意义上的数值库,这意味着这些库中的函数只能对特定的数值输入进行操作。它们不能与 SymPy 表达式一起使用,因为它们的算法不是为处理符号输入而设计的。SymPy 专注于符号函数,将纯粹的数值代码留给其他工具,如 NumPy。但是,SymPy 通过代码生成lambdify() 等工具与数值库进行交互。

Number

Number 在 SymPy 中可以指两件事

  • Number,它是显式数字(IntegerRationalFloat)的基类。像pi 这样的符号数值常数不是Number 的实例。

  • 小写“number”,如is_number 属性,指的是可以evalf 到显式Number 的任何表达式。这包括像pi 这样的符号常数。请注意,is_number 不是假设 系统的一部分。

这种区别对于is_Numberis_number 属性很重要。x.is_Number 会检查x 是否是类Number 的实例。

oo

oo 是表示正无穷大的 SymPy 对象。它的拼写方式是两个小写字母 O,因为它类似于符号 \(\infty\) 并且易于输入。另请参见zoo

Polys

polys 指的是sympy.polys 子模块,该模块实现了用于多项式操作的基本数据结构和算法。polys 是 SymPy 的关键部分(尽管通常不被认为是核心 部分),因为许多基本的符号操作可以表示为对多项式的操作。SymPy 中的许多算法都使用幕后的 polys。例如,factor() 是对 polys 中实现的多项式因式分解算法的封装。polys 中的类使用高效的数据结构实现,而不是像 SymPy 中的其他类那样是Basic 的子类。

Printing

打印是指将一个表达式转换为可以在屏幕上查看的形式。打印也常用于指代代码生成。SymPy 有几个打印器,它们使用不同的格式来表示表达式。一些更常用的打印器是字符串打印器(str())、漂亮打印器(pprint())、LaTeX 打印器(latex())和代码打印器。

关系

关系是一个表达式,它是一个符号等式(如 \(a=b\)),或一个符号不等式,如“小于”(\(a<b\))。等式 (\(=\)) 和不等式 (\(\neq\)) 关系分别通过 EqNe 创建。例如,Eq(x, 0) 表示 \(x=0\)。这些应该使用,而不是 ==!=,因为这些用于结构,而不是符号等式。不等式关系可以使用 <<=>>= 直接创建,如 x < 0

S

SymPy 中的 S 对象有两个用途

  • 它以属性的形式保存所有单例类。SymPy 中的一些特殊类是单例化的,这意味着它们始终只有一个实例。这是一种优化,可以节省内存。例如,Integer(0) 只有一个实例,它作为 S.Zero 可用。

  • 它作为sympify() 的简写,即 S(a)sympify(a) 相同。这对于将整数转换为 SymPy 整数以避免将 Python 整数相除(参见教程的注意事项部分)非常有用。

简化

简化(不要与sympify 混淆)是指将一个表达式转换为另一个在数学上等效但“更简单”的表达式。形容词“简单”实际上没有很好地定义。什么是更简单的取决于具体的用例和个人审美观。

SymPy 函数 simplify() 启发式地尝试各种简化算法以尝试找到表达式的“更简单”形式。如果你不特别关心从“简化”中得到什么,它可能很适合。但如果你对要应用的简化类型有所了解,通常最好使用一个或多个目标简化函数,这些函数对表达式应用非常具体的数学运算。

求解
求解器

求解一个方程或方程组是指找到一组表达式,当给定的符号用它们替换时,这些方程为真。例如,关于 \(x\) 的方程 \(x^2 = 1\) 的解将是集合 \(\{-1, 1\}\)。SymPy 可以使用不同的求解器 函数来求解不同类型的方程。例如,代数方程可以使用 solve() 求解,微分方程可以使用 dsolve() 求解,等等。

SymPy 通常使用“求解”和“求解器”来表示这种意义上的方程求解。它不以“解决问题”的意义使用。例如,人们通常更愿意说“计算积分”或“评估积分”,而不是“解决积分”来指代使用函数 integrate() 进行符号积分。

结构相等

如果两个 SymPy 对象作为表达式相等,即它们具有相同的表达式树,那么它们在结构上相等。两个结构相等的表达式被 SymPy 认为是相同的,因为所有 SymPy 表达式都是不可变的。可以使用 == 运算符检查结构相等,它总是返回 TrueFalse。符号等式可以使用 Eq 表示。

通常,如果两个表达式是相同的类并且(递归地)具有相同的参数,则它们在结构上是相等的。两个表达式在数学上可能相同,但在结构上并不相等。例如,(x + 1)**2x**2 + 2*x + 1 在数学上是相等的,但它们在结构上并不相等,因为第一个是 Pow,其参数由一个 Add 和一个 Integer 组成,第二个是 Add,其参数由一个 Pow、一个 Mul 和一个 Integer 组成。

如果两个明显不同的表达式被自动简化标准化为相同的东西,则它们在结构上可能是相等的。例如,x + yy + x 在结构上是相等的,因为 Add 构造函数会自动对它的参数进行排序,使它们都相同。

子表达式

子表达式是一个表达式,它包含在一个更大的表达式中。子表达式出现在表达式树的某个地方。对于 AddMul 项,在确定什么是子表达式时,可以考虑交换律和结合律。例如,x + y 有时可能被认为是 x + y + z 的子表达式,即使 Add(x, y) 的表达式树不是 Add(x, y, z) 的表达式树的直接子节点。

替换

替换指的是在一个符号子表达式内用另一个表达式将其替换。SymPy中有多种方法可以进行替换,包括subsreplacexreplace。这些方法可能有所不同,取决于它们是否只使用严格的结构相等来执行替换,或者在确定子表达式在表达式中出现的位置时使用数学知识。替换是将表达式视为数学函数并在某一点对其进行求值的一种标准方法。

符号

数学对象的符号表示是一种在运行时部分或完全未被求值的表示。它可能包含命名符号常量,代替显式的数值。符号表示通常与数值表示形成对比。符号表示在数学上是精确的,与数值表示形成对比,数值表示通常会被四舍五入,以适应浮点数的值。表示数学对象的符号表达式可能知道这些对象的数学属性,并且能够使用这些属性简化为等效的符号表达式。SymPy的目标是表示和操作表示各种数学对象的符号表达式。

一些资料使用“解析解”或“闭式”来指代“符号”的概念,但SymPy中不使用这种术语。如果在SymPy中使用,“解析”将指代是解析函数的属性,而在SymPy中,求解仅指代一种特定的符号操作类型。“闭式”在SymPy中通常指代该术语的数学意义,而“符号”通常指代数学概念的实现细节,并与相同数学概念的数值实现形成对比。

Symbol

Symbol是符号对象的类。符号表示表达式中的单个数学变量。Symbol类是Expr的子类,并且是原子。一个Symbol包含一个名称,可以是任何字符串,以及假设。符号通常使用Symbol构造函数或symbols()函数来定义。两个名称和假设相同的符号被认为是相等。符号被隐式地假定为彼此独立或恒定。常量、变量和参数都由符号表示。区别通常体现在符号在给定SymPy函数中的使用方式。

sympify()

sympify()(不要与simplify()混淆)是一个函数,它将非SymPy对象转换为SymPy对象。sympify()的结果将是Basic的实例。可以sympify的对象包括本机Python数值类型,如intfloat,可以解析为SymPy表达式的字符串,以及包含sympifiable对象的迭代器(有关更多信息,请参见sympify()的文档)。

由于所有SymPy表达式都必须是Basic的实例,因此所有SymPy函数和操作都会隐式地对其输入调用sympify()。例如,x + 1隐式地调用sympify(1),将Pythonint1转换为SymPyInteger。接受SymPy表达式的函数通常应该在其参数上调用sympify(),这样即使输入不是SymPy类型,它们也能正常工作。

三值逻辑

三值逻辑是一种具有三个值的逻辑,分别是TrueFalseNone。它有时也被称为模糊逻辑,尽管这个术语在数学文献中也有不同的含义,所以“三值逻辑”是首选。“True”和“False”在通常的二值谓词逻辑中与平常一样。“None”是一个额外的术语,代表“未知”、“不可计算”或“可能为真或假”(从哲学上讲,这些是不同的概念,但在逻辑上,它们的运作方式完全相同)。None的语义是,在逻辑运算中,它会吸收其他术语,只要结果与用TrueFalse替换它时不同。例如,None OR FalseNone,但None OR TrueTrue,因为无论None“真正”代表True还是False的值,谓词都是True。在对三值逻辑使用通常的Python逻辑运算符(如andornot)时,必须小心,因为None是假的。有关如何在三值逻辑中进行编码的更多详细信息,请参见符号和模糊布尔值的指南

三值逻辑被假设系统用来表示未知的假设。例如,如果在给定的假设下,x可能为正或负,那么x.is_positive可能为None。请注意,由布尔子类定义的谓词逻辑代表的是标准的二值逻辑,而不是三值逻辑。

未定义函数

未定义函数是一个函数,它没有定义任何数学属性。它始终保持未被求值,例如f(x)。未定义函数可以通过将函数的字符串名称传递给Function来创建,例如f = Function('f')。在处理ODE时,未定义函数通常被使用。未定义函数也是创建数学上依赖于其他符号的符号的最简单方法。例如,如果f = Function('f'),并且x = Symbol('x'),那么SymPy将知道f(x)依赖于x,这意味着例如,导数diff(f(x), x)将不会被求值为0

未被求值

如果表达式创建时通常发生的 自动简化 被禁用,则该表达式为 未计算。这通常通过设置 evaluate=False、使用 with evaluate(False) 或使用 UnevaluatedExpr 来完成。虽然支持未计算的表达式,但它们有时会导致意外的行为,因为表达式没有被正确地 规范化

术语 未计算 也有时用来表示表达式在它的参数是 符号 时不会 计算 为特定值。

zoo

zoo 代表 复无穷大,即 黎曼球面 的北极。它之所以这样拼写是因为它是“z-oo”,其中“z”是通常用于复数变量的符号,而 oo 是 SymPy 用来表示实数正无穷大的符号。