2016-01-27 28 views
9

今天,我發現這個Sympy類Zero,One和NegativeOne,它們爲什麼存在?

>>> 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

+0

到底什麼你做把'type(1)'變成'int'以外的任何東西? – user2357112

+0

@ user2357112剛剛導入sympy模塊 –

+0

導入sympy不會像這樣解析器。 'type(1)'將始終構造一個實際的Python整數,並將其類型設置爲'int'。 – user2357112

回答

5

在SymPy每個數字是由the class Number實例表示。 Float s,Integer s和Rational s是Number的子類。 ZeroInteger的 的子類。

你可以通過調用其類的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] 

這些子類「教」 SymPy如何操作和象徵性地簡化表達式。作爲 例如,在Rational類的實例是negated this way

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

也就是說,如果xRational一個實例,然後-x導致x.__neg__()被調用。

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

ZeroOneMinusOne:同時,Integer類的實例,是negated by

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

而如果對象是,特別的Zero一個實例,則its negation是 由下式定義還實施_eval_power方法,其中 「教」這些對象如何評估x提高到一個權力(其中xZero,OneMinusOne)。例如,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 

如果你仔細閱讀the source codesympy.core.numbers模塊,你會發現 負荷的定義它們是效應教學SymPy如何算術符號 。這與數學課中教孩子的東西並沒有太大差別,只不過它是用計算機來表達的。

您可能想知道爲什麼每個整數都沒有特殊的類。 Integers除了Zero,OneMinusOne被視爲 通用Integer類的實例。他們的加法和乘法規則等都在那裏列出。不像在加載模塊時被instantated 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 
+0

哇,很好的答案。還有一個問題,就是按需緩存「Zero」,「One」和「NegativeOne」旁邊的其他整數,這就是爲什麼它總是返回相同的內存地址(帶'id'函數)的原因?這是對每個Integer實例進行「單例」的一種類型嗎? –

+1

是的。 '_intcache'是一個(私有)'dict',它將Python整數映射到SymPy'Integer'。每當'Integer'被實例化時,首先檢查_intcache'。如果該整數已經作爲'_intcache'中的鍵存在,那麼返回'Integer'。 – unutbu

3

首先,請注意type(1)給你type(Integer(1))因爲SymPy現場包裝在Integer()自動整型常量(這是爲了避免其中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。我猜他們增加了更多的方便性大於任何東西)

+0

儘管int自動換行肯定會讓該特定陷阱的「運行代碼塊在SymPy Live」鏈接中變得非常愚蠢。 SymPy Live與文檔試圖解釋的行爲相反。 – user2357112

+0

哈好點。我打開了https://github.com/sympy/sympy/issues/10484。 – asmeurer

相關問題