Vérification Rapide
De le des signatures, nous pouvons dire qu'ils sont différents:
pow(x, y, z])
les mathématiques.pow(x, y)
Aussi, en essayant dans le shell va vous donner une petite idée:
>>> pow is math.pow
False
Tester les différences
Une autre façon de comprendre les différences de comportement entre les deux fonctions est de les tester:
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()
Ensuite, nous pouvons remarquer quelques différences subtiles. Par exemple:
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
Il y a d'autres différences, et le test de la liste ci-dessus n'est pas complet (pas de numéros longs, pas de complexes, etc...), mais cela va nous donner une liste pratique de la façon dont les deux fonctions se comportent différemment. Je vous recommande également d'étendre le test ci-dessus pour vérifier le type de chaque fonction renvoie. On pourrait peut-être écrire quelque chose de semblable qui crée un rapport de la différence entre les deux fonctions.
math.pow()
math.pow()
gère ses arguments de façon très différente de la builtin **
ou pow()
. Cela se fait au détriment de la souplesse. Avoir un coup d'oeil à la source, nous pouvons voir que la arguments au math.pow()
sont exprimés directement à doubles:
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);
/*...*/
Les contrôles sont ensuite menées contre le double pour la validité et le résultat est transmis à la sous-jacentes C de la bibliothèque math.
builtin pow()
Le haut- pow()
(la même que l' **
opérateur) sur l'autre main se comporte de manière très différente, en fait, il utilise les Objets propres de la mise en œuvre de l' **
de l'opérateur, qui peut être remplacée par l'utilisateur final en cas de nécessité par le remplacement d'un certain nombre de __pow__()
, __rpow__()
ou __ipow__()
, méthode.
Pour les types intégrés, il est instructif d'étudier la différence entre la fonction de puissance mis en œuvre pour les deux types numériques, par exemple, des flotteurs, long et complexe.
Overridding le comportement par défaut
L'émulation de types numériques est décrit ici. essentiellement, si vous êtes à la création d'un nouveau type pour les nombres à l'incertitude, ce que vous avez à faire est de fournir à l' __pow__()
, __rpow__()
et, éventuellement, __ipow__()
méthodes pour votre type. Cela permettra à vos numéros pour être utilisé avec l'opérateur:
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
)
Afin de remplacer math.pow()
vous aurez à monkey patch à l'appui de votre nouveau type:
def new_pow(a,b):
_a = Uncertain(a)
_b = Uncertain(b)
return _a ** _b
math.pow = new_pow
Notez que pour que cela fonctionne, vous devez régler l' Uncertain
classe pour faire face à un Uncertain
exemple comme un intrant __init__()