程式扎記: [Python Std Library] Numeric and Math Modules : numbers — Numeric abstract base classes

標籤

2012年4月9日 星期一

[Python Std Library] Numeric and Math Modules : numbers — Numeric abstract base classes


翻譯自 這裡
Preface :
New in version 2.6.
numbers 模組 (PEP 3141) 定義了階層式的 abstract base classes. 首先來看在此模組最頂層的類別 :
- class numbers.Number
The root of the numeric hierarchy. If you just want to check if an argument x is a number, without caring what kind, use isinstance(x, Number).

The numeric tower :
接著來看模組定義的其它類別 :
- class numbers.Complex (複數)
Subclasses of this type describe complex numbers and include the operations that work on the built-in complex type. These are: conversions to complex and bool,realimag+-*/abs()conjugate()==, and !=. All except - and != are abstract.

- class numbers.Real (實數)
To ComplexReal adds the operations that work on real numbers.

In short, those are: a conversion to floatmath.trunc()round()math.floor()math.ceil()divmod()//%<<=>, and >=. Real also provides defaults for complex(),realimag, and conjugate().

- class numbers.Rational (有理數)
Subtypes Real and adds numerator and denominator properties, which should be in lowest terms. With these, it provides a default for float().

- class numbers.Integral (整數)
Subtypes Rational and adds a conversion to int. Provides defaults for float()numerator, and denominator, and bit-string operations: <<>>&^|~.

Notes for type implementors :
在實作這些抽象類別時需要注意某些地方. 首先就是同樣數值大小的 __equal__() 與 __hash__() 返回的值也必須要一樣! 可以參考下面 fractions.Fraction 如何實作 hash() 當作參考 :
  1. def __hash__(self):  
  2.     if self.denominator == 1:  
  3.         # Get integers right.  
  4.         return hash(self.numerator)  
  5.     # Expensive check, but definitely correct.  
  6.     if self == float(self):  
  7.         return hash(float(self))  
  8.     else:  
  9.         # Use tuple's hash to avoid a high collision rate on  
  10.         # simple fractions.  
  11.         return hash((self.numerator, self.denominator))  
- Implementing the arithmetic operations
在你繼承 Integral 並實作上面的方法時, 需要注意方法 __add__() 與 __radd__() 的差別, 底下為範例代碼 :
  1. class MyIntegral(Integral):  
  2.   
  3.     def __add__(self, other):  
  4.         if isinstance(other, MyIntegral):  
  5.             return do_my_adding_stuff(self, other)  
  6.         elif isinstance(other, OtherTypeIKnowAbout):  
  7.             return do_my_other_adding_stuff(self, other)  
  8.         else:  
  9.             return NotImplemented  
  10.   
  11.     def __radd__(self, other):  
  12.         if isinstance(other, MyIntegral):  
  13.             return do_my_adding_stuff(other, self)  
  14.         elif isinstance(other, OtherTypeIKnowAbout):  
  15.             return do_my_other_adding_stuff(other, self)  
  16.         elif isinstance(other, Integral):  
  17.             return int(other) + int(self)  
  18.         elif isinstance(other, Real):  
  19.             return float(other) + float(self)  
  20.         elif isinstance(other, Complex):  
  21.             return complex(other) + complex(self)  
  22.         else:  
  23.             return NotImplemented  
接著我們要來看看 __radd__() 方法中的 if/else 區塊. 假設我們有定義類別 AB 繼承自 Integer ; 而 A 的生成物件為 a, B 的生成物件 b, 考慮 a + b :
1. If A defines an __add__() which accepts b, all is well.
2. If A falls back to the boilerplate code (For other as MyIntegral or OtherTypeIKnowAbout), and it were to return a value from __add__(), we’d miss the possibility that B defines a more intelligent __radd__(), so the boilerplate should return NotImplemented from __add__(). (Or A may not implement __add__() at all.)
3. Then B‘s __radd__() gets a chance. If it accepts a, all is well.
4. If it falls back to the boilerplate, there are no more possible methods to try, so this is where the default implementation should live.
5. If B <: A (B 繼承自 A), Python tries B.__radd__ before A.__add__. This is ok, because it was implemented with knowledge of A, so it can handle those instances before delegating to Integer.

因為類似的邏輯可以應用在許多運算子上, 所以透過底下的轉換便可以將這樣的邏輯替換到許多運算子對應的函數上面 (參考自 fractions.Fraction):
  1. def _operator_fallbacks(monomorphic_operator, fallback_operator):  
  2.     def forward(a, b):  
  3.         if isinstance(b, (intlong, Fraction)):  
  4.             return monomorphic_operator(a, b)  
  5.         elif isinstance(b, float):  
  6.             return fallback_operator(float(a), b)  
  7.         elif isinstance(b, complex):  
  8.             return fallback_operator(complex(a), b)  
  9.         else:  
  10.             return NotImplemented  
  11.     forward.__name__ = '__' + fallback_operator.__name__ + '__'  
  12.     forward.__doc__ = monomorphic_operator.__doc__  
  13.   
  14.     def reverse(b, a):  
  15.         if isinstance(a, Rational):  
  16.             # Includes ints.  
  17.             return monomorphic_operator(a, b)  
  18.         elif isinstance(a, numbers.Real):  
  19.             return fallback_operator(float(a), float(b))  
  20.         elif isinstance(a, numbers.Complex):  
  21.             return fallback_operator(complex(a), complex(b))  
  22.         else:  
  23.             return NotImplemented  
  24.     reverse.__name__ = '__r' + fallback_operator.__name__ + '__'  
  25.     reverse.__doc__ = monomorphic_operator.__doc__  
  26.   
  27.     return forward, reverse  
  28.   
  29. def _add(a, b):  
  30.     """a + b"""  
  31.     return Fraction(a.numerator * b.denominator +  
  32.                     b.numerator * a.denominator,  
  33.                     a.denominator * b.denominator)  
  34.   
  35. __add__, __radd__ = _operator_fallbacks(_add, operator.add)  
  36.   
  37. # ...  

沒有留言:

張貼留言

網誌存檔

關於我自己

我的相片
Where there is a will, there is a way!