クイックチェック
署名から、それらが異なることがわかります。
pow(x, y[, z])
math.pow(x, y)
また、シェルで試してみると、簡単なアイデアが得られます。
>>> pow is math.pow
False
違いをテストする
2 つの関数の動作の違いを理解する別の方法は、それらをテストすることです。
import math
import traceback
import sys
inf = float("inf")
NaN = float("nan")
vals = [inf, NaN, 0.0, 1.0, 2.2, -1.0, -0.0, -2.2, -inf, 1, 0, 2]
tests = set([])
for vala in vals:
for valb in vals:
tests.add( (vala, valb) )
tests.add( (valb, vala) )
for a,b in tests:
print("math.pow(%f,%f)"%(a,b) )
try:
print(" %f "%math.pow(a,b))
except:
traceback.print_exc()
print("__builtins__.pow(%f,%f)"%(a,b) )
try:
print(" %f "%__builtins__.pow(a,b))
except:
traceback.print_exc()
その後、いくつかの微妙な違いに気付くことができます。例えば:
math.pow(0.000000,-2.200000)
ValueError: math domain error
__builtins__.pow(0.000000,-2.200000)
ZeroDivisionError: 0.0 cannot be raised to a negative power
他にも違いがあり、上記のテスト リストは完全ではありません (長い数値がない、複雑でないなど) が、これにより、2 つの関数の動作がどのように異なるかの実用的なリストが得られます。上記のテストを拡張して、各関数が返す型を確認することもお勧めします。おそらく、2 つの関数の相違点のレポートを作成する同様のものを作成できます。
math.pow()
math.pow()
**
は、組み込みのorとは非常に異なる方法で引数を処理しますpow()
。これには、柔軟性が犠牲になります。sourceを見ると、引数math.pow()
がdouble に直接キャストされていることがわかります。
static PyObject *
math_pow(PyObject *self, PyObject *args)
{
PyObject *ox, *oy;
double r, x, y;
int odd_y;
if (! PyArg_UnpackTuple(args, "pow", 2, 2, &ox, &oy))
return NULL;
x = PyFloat_AsDouble(ox);
y = PyFloat_AsDouble(oy);
/*...*/
次に、double の有効性についてチェックが実行され、その結果が基になる C 数学ライブラリに渡されます。
ビルトインpow()
一方、ビルトインpow()
(演算子と同じ**
) は非常に異なる動作をします。実際にはオブジェクト独自の**
演算子の実装を使用します。これは、必要に応じてエンド ユーザーが数値の__pow__()
,__rpow__()
または__ipow__()
, メソッドを置き換えることでオーバーライドできます。
組み込み型の場合、 floats、long、およびcomplexなどの 2 つの数値型に実装された累乗関数の違いを調べることは有益です。
デフォルトの動作のオーバーライド
数値型のエミュレートについては、こちらで説明しています。基本的に、不確実性を伴う数値の新しい型を作成する場合は、型に__pow__()
、__rpow__()
および場合によっては__ipow__()
メソッドを提供する必要があります。これにより、数値を演算子で使用できるようになります。
class Uncertain:
def __init__(self, x, delta=0):
self.delta = delta
self.x = x
def __pow__(self, other):
return Uncertain(
self.x**other.x,
Uncertain._propagate_power(self, other)
)
@staticmethod
def _propagate_power(A, B):
return math.sqrt(
((B.x*(A.x**(B.x-1)))**2)*A.delta*A.delta +
(((A.x**B.x)*math.log(B.x))**2)*B.delta*B.delta
)
オーバーライドmath.pow()
するには、新しいタイプをサポートするためにモンキー パッチを適用する必要があります。
def new_pow(a,b):
_a = Uncertain(a)
_b = Uncertain(b)
return _a ** _b
math.pow = new_pow
これを機能させるには、Uncertain
クラスをラングリングして、Uncertain
インスタンスへの入力として処理する必要があることに注意してください。__init__()