web-dev-qa-db-fra.com

Exponentiation in Python - devrais-je préférer l'opérateur ** au lieu de math.pow et math.sqrt?

Dans mon domaine, il est très courant de mettre quelques chiffres en carré, de les exploiter ensemble et de prendre la racine carrée du résultat. Ceci est fait dans le théorème de Pythagore et le calcul RMS, par exemple).

Numpy, j'ai fait ce qui suit:

result = numpy.sqrt(numpy.sum(numpy.pow(some_vector, 2)))

Et en pure python on pourrait s'attendre à quelque chose comme ceci:

result = math.sqrt(math.pow(A, 2) + math.pow(B,2)) # example with two dimensions.

Cependant, j'utilisais cette forme pure python, car je la trouve beaucoup plus compacte, indépendante de l'importation et apparemment équivalente:

result = (A**2 + B**2)**0.5   # two dimensions
result = (A**2 + B**2 + C**2 + D**2)**0.5

J'ai entendu certaines personnes dire que l'opérateur ** Est en quelque sorte un hack, et que cadrer un nombre en l'exponentiant de 0.5 N'est pas aussi lisible. Mais ce que je voudrais demander, c'est si:

"Y a-t-il une raison COMPUTATIONNELLE de préférer les deux premières alternatives au (x) troisième (s)?"

Merci d'avoir lu!

56
heltonbiker

math.sqrt est l'implémentation C de la racine carrée et est donc différent de l'opérateur ** qui implémente la fonction intégrée pow de Python. Ainsi, utiliser math.sqrt donne en réalité une réponse différente de celle de l'opérateur ** et il existe en effet une raison de calcul de préférer la mise en œuvre du module numpy ou math à celle intégrée. Plus précisément, les fonctions sqrt sont probablement implémentées de la manière la plus efficace possible, alors que ** fonctionne sur un grand nombre de bases et d’exposants et n’est probablement pas optimisé pour le cas spécifique de la racine carrée. D'autre part, la fonction intégrée pow gère quelques cas supplémentaires tels que "nombres complexes, puissances entières non bornées et exponentiation modulaire".

Voir cette question de débordement de pile pour plus d'informations sur la différence entre ** et math.sqrt.

En ce qui concerne ce qui est plus "pythonique", je pense que nous devons discuter de la définition même de ce mot. De le fonctionnaire Python glossary , il est indiqué qu'un morceau de code ou une idée est Pythonic s'il "suit de près les idiomes les plus courants du Python langue, plutôt que d’implémenter du code utilisant des concepts communs à d’autres langues. "Dans toutes les autres langues auxquelles je peux penser, il existe un module de mathématiques avec des fonctions de base de la racine carrée. Cependant, il existe des langues qui manquent d’un opérateur comme ** Par exemple, C++. Donc ** est probablement plus Pythonic, mais son objectivité est meilleure dépendamment du cas d'utilisation.

53
Shashank

Même en base Python vous pouvez faire le calcul sous forme générique

result = sum(x**2 for x in some_vector) ** 0.5

x ** 2 n'est sûrement pas un hack et le calcul effectué est le même (j'ai vérifié avec le code source cpython). En fait, je le trouve plus lisible (et la lisibilité compte).

Utiliser à la place x ** 0.5 prendre la racine carrée ne fait pas exactement les mêmes calculs que math.sqrt comme le premier est (probablement) calculé en utilisant des logarithmes et le dernier (probablement) en utilisant l’instruction numérique spécifique du processeur mathématique.

J'utilise souvent x ** 0.5 simplement parce que je ne veux pas ajouter math rien que pour ça. Je m'attendrais cependant à ce qu'une instruction spécifique pour la racine carrée fonctionne mieux (plus précisément) qu'une opération à plusieurs étapes avec des logarithmes.

16
6502