Sympy类Zero,One和NegativeOne,为什么它们存在?

时间:2016-01-27 17:47:31

标签: python sympy

今天我发现了这个

>>> type(1)
<class 'sympy.core.numbers.One'>
>>> type(0)
<class 'sympy.core.numbers.Zero'>
>>> type(-1)
<class 'sympy.core.numbers.NegativeOne'>
>>> type(2)
<class 'sympy.core.numbers.Integer'>

我看了documentation from sympy关于这些类型的内容,但它没有说明它们存在的原因。是否有理由为-1,0和1设置3个特殊单例类?

编辑:我在SymPy online shell

看到了这一点

2 个答案:

答案 0 :(得分:5)

SymPy中的每个数字都由the class Number的实例表示。 Float s,IntegerRationalNumber的子类。 Zero是一个 Integer的子类。

您可以通过调用其类mro(方法解析顺序)方法来检查对象的完整类别:

In [34]: from sympy import S
In [38]: type(S.Zero).mro()
Out[38]: 
[sympy.core.numbers.Zero,
 sympy.core.numbers.IntegerConstant,
 sympy.core.numbers.Integer,            <-- Zero is a kind of Integer
 sympy.core.numbers.Rational,
 sympy.core.numbers.Number,
 sympy.core.expr.AtomicExpr,
 sympy.core.basic.Atom,
 sympy.core.expr.Expr,
 sympy.core.basic.Basic,
 sympy.core.evalf.EvalfMixin,
 object]

这些子类&#34;教导&#34; SymPy如何象征性地操纵和简化表达式。作为一个 例如,Rational类的实例是negated this way

def __neg__(self):
    return Rational(-self.p, self.q)

也就是说,如果xRational的实例,那么-x会导致x.__neg__()被调用。同时,Integer类的实例为negated by

def __neg__(self):
    return Integer(-self.p)

如果对象特别是Zero的实例,那么its negation就是。{ 定义如下:

@staticmethod
def __neg__():
    return S.Zero    # the negation of Zero is still Zero

ZeroOneMinusOne也实施了_eval_power方法 &#34;教导&#34;这些对象如何评估x提升到权力(x所在的位置 ZeroOneMinusOne)。例如,Zero raised to a positive expression等于自己:

def _eval_power(self, expt):
    if expt.is_positive:
        return self
    ...

One raised to anything等于自己:

def _eval_power(self, expt):
    return self

如果您仔细阅读sympy.core.numbers模块the source code,则可以找到 实际上教导SymPy如何做符号的定义 算术。它与数学课上的孩子教学方式没什么不同,只不过它是用计算机表达的。

您可能想知道为什么没有针对每个整数的特殊类。 除Integers之外的ZeroOneMinusOne被视为该实例 一般Integer类。他们的加法和乘法规则等都列在那里。与加载模块时即时创建的ZeroOneMinusOne不同,其他整数被缓存only as needed

def __new__(cls, i):
    ...
    try:
        return _intcache[ival]   # <-- return the cached Integer if seen before
    except KeyError:           
        obj = Expr.__new__(cls)  # <-- create a new Integer if ival not in _intcache
        obj.p = ival

        _intcache[ival] = obj
        return obj

答案 1 :(得分:3)

首先,请注意type(1)为您提供了type(Integer(1)),因为SymPy Live会自动包装Integer()中的整数文字(这是为了避免gotcha 1/2评估到0.5而不是Rational(1, 2))。但请注意,在常规Python会话中,type(1)int

SymPy中有几个对象实现为单例,这意味着只存在一个实例。您可以在S对象

上看到这些内容
In [13]: dir(S)
Out[13]:
['Catalan',
 'ComplexInfinity',
 'Complexes',
 'EulerGamma',
 'Exp1',
 'GoldenRatio',
 'Half',
 'IdentityFunction',
 'ImaginaryUnit',
 'Infinity',
 'NaN',
 'Naturals0',
 'NegativeInfinity',
 'NegativeOne',
 'One',
 'Pi',
 'Reals',
 'Zero',
 '__call__',
 '__class__',
 '__delattr__',
 '__doc__',
 '__format__',
 '__getattr__',
 '__getattribute__',
 '__hash__',
 '__init__',
 '__module__',
 '__new__',
 '__reduce__',
 '__reduce_ex__',
 '__repr__',
 '__setattr__',
 '__sizeof__',
 '__slots__',
 '__str__',
 '__subclasshook__',
 '_classes_to_install',
 'false',
 'register',
 'true']

(忽略以_开头的那些;那些是Python内部方法)

这样做的原因是这些对象被大量使用。 0,1和-1是非常常见的对象。每次撰写1/x时,它都会在内部表示为Pow(x, -1)x - y表示为Add(x, Mul(-1, y))。对于0,它经常出现在各种符号计算中。 1也很常见。通过使用单个实例,SymPy可以实现两个优化。首先,它节省了内存。其次,您可以使用is比较来比较这些对象,例如x is S.One。因为只有一个实例可以存在Integer(1)始终与S.One相同。

(另外,我应该注意S中的某些对象实际上并不常见,例如CatalanEulerGamma。我猜他们为了方便而添加了更多内容。 )