(二)使用SymPy需要避开的坑
首先,我们应该清楚,SymPy和NumPy、Django一样,不过是一个Python库。这意味着SymPy没有向Python语言添加任何内容。Python语言固有的限制也是SymPy固有的。例如:Python中不允许隐式乘法(如3x
或3 x
),因此SymPy中也不允许隐式乘法。要将3和x相乘,必须用*
键入3*x
。
1 Symbols
SymPy可以在Python可用的任何环境中使用。我们只是导入它,就像导入其他库一样:
In [1]: from sympy import *
现在进行一个运算:
In [2]: x + 1
---------------------------------------------------------------------------
NameError
...
NameError: name 'x' is not defined
我们试图使用变量x
,但它告诉我们x
没有定义。在Python中,变量在定义之前没有任何意义。SymPy也不例外,它的变量不是自动定义的。为了定义变量,我们必须使用符号symbols
.。
In [3]: x = symbols('x')
In [4]: x+1
Out[4]: x + 1
symbols
接受由空格或逗号分隔的变量名字符串,并从中创建符号。然后我们可以将它们赋给变量名。
让我们定义最常见的变量名x
、y
和z
,以便在本文的其余部分中使用
In [5]: x, y, z = symbols('x y z')
注意:符号的名称和它所分配的变量的名称不必相同
In [6]: a, b = symbols('b a')
In [7]: a
Out[7]: b
In [8]: b
Out[8]: a
符号的名称可以超过一个字符:
In [9]: crazy = symbols('unrelated')
In [10]: crazy + 1
Out[10]: unrelated + 1
通常,最好将符号分配给同名的Python变量,不过也有例外:符号名称可以包含Python变量名称中不允许的字符(比如:1x
),或者可能只是希望通过将长名称的符号分配给单字母Python变量来避免键入长名称。
最后,为了确保读者理解SymPy符号和Python变量之间的区别。我们来看以下例子:
In [12]: x = symbols('x')
In [13]: expr = x + 1
In [14]: x=2
In [15]: print(expr)
x + 1
将x
改为2对expr
没有影响。这是因为x=2
改变的是Python变量x
,而对SymPy符号x
没有影响,它是我们在创建expr
时使用的符号。
当我们创建expr
时,Python变量x
是一个符号。创建之后,我们将Python变量x
更改为2。但是expr
保持不变。这种行为并不是SymPy独有的。所有Python程序都是这样工作的:如果一个变量被更改,已经用该变量创建的表达式不会自动更改。例如
In [16]: x = 'abc'
In [17]: expr = x + 'def'
In [18]: expr
Out[18]: 'abcdef'
In [19]: x = 'ABC'
In [20]: expr
Out[20]: 'abcdef'
Tip:要更改表达式中符号的值,使用subs
In [21]: x = symbols('x')
In [22]: expr = x + 1
In [23]: expr.subs(x, 2)
Out[23]: 3
2 等号
SymPy没有扩展Python语法,这导致=
在SymPy中也不表示相等,而是Python变量赋值。这是硬编码(hard-coded)到Python语言中的,SymPy没有试图改变这一点。
您可能认为,Python中用于等式测试的== ,被SymPy用作等式。这也不完全正确。
当我们使用==
时:
In [4]: x + 1 == 4
Out[4]: False
我们没有得到符号表达式x+1==4
,而是得到了False
。在SymPy中,==
表示精确的结构相等性测试。这意味着a==b
相当于我们在问 a是否等于b。我们总是得到一个布尔值作为==
的结果。
要创建符号等式,可以通过创建Eq
对象:
In [5]: Eq(x + 1, 4)
Out[5]: Eq(x + 1, 4)
关于==
还一点需要注意:假设我们想知道
(
x
+
1
)
2
=
x
2
+
2
x
+
1
(x+1)^{2}=x^{2}+2 x+1
(x+1)2=x2+2x+1是否成立。我们可能会这样做:
In [6]: (x + 1)**2 == x**2 + 2*x + 1
Out[6]: False
回想一下上面的==
表示精确的结构相等性测试。这里的“精确”意味着只有当两个表达式在结构上完全相等时,它们用==
比较才会得到True
。
这里, ( x + 1 ) 2 (x+1)^2 (x+1)2和 x 2 + 2 x + 1 x^2+2x+1 x2+2x+1在结构上是不同的。一种是两项相加的幂,另一种是三项相加的幂。
我们有我们已经看到了一个替代符号表示相等的方法,Eq
。我们知道,如果a=b
,那么a−b=0
。因此,检查a=b的最佳方法是取a−b并将其简化,然后看看它是否为0。实现简化的函数称为simplify
。
In [7]: a = (x + 1)**2
In [8]: b = x**2 + 2*x + 1
In [9]: simplify(a - b)
Out[9]: 0
In [10]: c = x**2 - 2*x + 1
In [11]: simplify(a - c)
Out[11]: 4*x
这种方法并不是绝对正确的,但对于大多数常见的表达式,它工作得相当好。
还有一种称为equals
的方法,通过在随机点上对两个表达式进行数值求值来检验它们是否相等。
In [12]: a = cos(x)**2 - sin(x)**2
In [13]: b = cos(2*x)
In [14]: a.equals(b)
Out[14]: True
3 ^ 和 /
您可能已经注意到,我们一直使用**
而不是标准的^
,这是因为SymPy遵循Python的约定。在Python中,^
表示逻辑异或。
SymPy也一样:
In [15]: True ^ False
Out[15]: True
In [16]: True ^ True
Out[16]: False
In [17]: Xor(x, y)
Out[17]: Xor(x, y)
最后,就SymPy的工作原理进行一个小的技术讨论。
In [18]: type(Integer(1) + 1)
Out[18]: sympy.core.numbers.Integer
In [19]: type(1 + 1)
Out[19]: int
每当你组合一个SymPy对象和一个SymPy对象,或者一个SymPy对象和一个Python对象,你就得到了一个SymPy对象,但是每当你组合两个Python对象,SymPy永远不会起作用,所以你就得到了一个Python对象。
在SymPy中,两个整数的除法给出一个分数:
In [21]: Integer(1)/Integer(3)
Out[21]: 1/3
In [22]: type(Integer(1)/Integer(3))
Out[22]: sympy.core.numbers.Rational
但在Python/中,表示整数除法或浮点除法,这取决于您是在Python 2中还是Python 3中,以及您是否python2中执行from __future__ import division
运行:
In [25]: 1/2
Out[25]: 0.5
运行上述示例时,1/2
输出的是0.5,而不是1/2
为了避免这种情况,我们可以显式地构造rational对象
In [26]: Rational(1, 2)
Out[26]: 1/2
每当我们编写一个包含int/int
的符号表达式时,这个问题也会出现。例如:
In [27]: x + 1/2
Out[27]: x + 0.5
这是因为Python首先将1/2求值为0.5,然后将其添加到x
时将其转换为SymPy类型。
同样,我们可以通过显式创建一个Rational:
In [28]: x + Rational(1, 2)
Out[28]: x + 1/2
感谢阅读。
未完待续:
【SymPy】(三)基本操作
【SymPy】(四)打印机
【SymPy】(五)简化
【SymPy】(六)微积分
【SymPy】(七)方程求解
【SymPy】(八)矩阵
【SymPy】(九)高级表达式操作