Python中内置的pow()和math.pow()之间的差异?

Eri*_*got 62 python math

在两个float参数的情况下,Python的内置pow(x, y)(没有第三个参数)和返回的值返回的结果是否有差异.math.pow()

我问这个问题,因为文件math.pow()暗示pow(x, y)(例如x**y)在本质上是一样的math.pow(x, y):

math.pow(x,y)

返回x上升到幂y.特殊情况尽可能遵循C99标准的附录'F'.特别是,即使x为零或NaN,pow(1.0,x)和pow(x,0.0)也总是返回1.0.如果x和y都是有限的,x是负的,y不是整数,则pow(x,y)是未定义的,并引发ValueError.

在版本2.6中更改:1**nan和nan**0的结果未定义.

注意最后一行:文档暗示行为math.pow()是指数运算符**(因此pow(x, y))的行为.这是官方保证吗?

背景:我的目标是提供一个实现这两个内置的pow()math.pow()对不确定性的数字,在以同样的方式表现与常规的Python花车(相同的数值结果,同样的例外,拐角情况相同的结果,等等).我已经已经实现的东西,工作得很好,但也有一些角落情况下,需要进行处理.

bri*_*ice 54

快速检查

从签名中,我们可以看出它们是不同的:

pow(x,y [,z])

math.pow(x,y)

此外,在shell中尝试它会给你一个快速的想法:

>>> pow is math.pow
False
Run Code Online (Sandbox Code Playgroud)

测试差异

理解两个函数之间行为差异的另一种方法是测试它们:

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()
Run Code Online (Sandbox Code Playgroud)

然后我们可以注意到一些微妙的差异.例如:

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
Run Code Online (Sandbox Code Playgroud)

还有其他差异,上面的测试列表并不完整(没有长数字,没有复杂等等),但这将为我们提供一个实用的列表,列出两个函数的行为方式不同.我还建议扩展上面的测试以检查每个函数返回的类型.您可能会编写类似的内容,以创建两个函数之间差异的报告.

math.pow()

math.pow()处理它的参数与内置**pow().这是以灵活性为代价的.在看看,我们可以看到,参数math.pow()直接转换为双打:

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);
/*...*/
Run Code Online (Sandbox Code Playgroud)

然后针对有效性的双精度执行检查,然后将结果传递给基础C数学库.

内置 pow()

内置的pow()(相同**,另一方面运营商)的行为非常不同,它实际上使用的对象本身执行的**操作,可以由最终用户覆盖在有需要时更换号码的__pow__(),__rpow__()或者__ipow__(),方法.

对于内置类型,研究为两种数值类型实现的幂函数之间的差异是有益的,例如浮点数,长数复数.

覆盖默认行为

此处描述模拟数字类型.基本上,如果你正在创建具有不确定性数字的新的类型,你将需要做的就是提供__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
    )
Run Code Online (Sandbox Code Playgroud)

为了覆盖math.pow()你将不得不修补它以支持你的新类型:

def new_pow(a,b):
    _a = Uncertain(a)
    _b = Uncertain(b)
    return _a ** _b

math.pow = new_pow
Run Code Online (Sandbox Code Playgroud)

请注意,要使其正常工作,您必须与Uncertain类进行争吵,以便将Uncertain实例作为输入来处理__init__()


dan*_*n04 32

math.pow()隐式地将其参数转换为float:

>>> from decimal import Decimal
>>> from fractions import Fraction
>>> math.pow(Fraction(1, 3), 2)
0.1111111111111111
>>> math.pow(Decimal(10), -1)
0.1
Run Code Online (Sandbox Code Playgroud)

但内置的pow不是:

>>> pow(Fraction(1, 3), 2)
Fraction(1, 9)
>>> pow(Decimal(10), -1)
Decimal('0.1')
Run Code Online (Sandbox Code Playgroud)

我的目标是为具有不确定性的数字提供内置pow()和math.pow()的实现

可以重载pow**通过定义__pow____rpow__方法,为你的类.

但是,你不能超载math.pow(没有像黑客一样math.pow = pow).您可以math.pow通过定义__float__转换来使类可用,但随后您将失去与数字相关的不确定性.

  • 您可能希望添加`math.pow`只是因为`math`模块反映了标准C的数学库. (5认同)
  • 好答案.FWIW,在CPython源代码中,[内置pow函数](http://hg.python.org/cpython/file/c7163a7f7cd2/Python/bltinmodule.c#l1505)调用`PyNumber_Power`,调用`__pow__ `对有问题的对象.例如,请参阅[float_pow的源代码](http://hg.python.org/cpython/file/9a84cab7ff9d/Objects/floatobject.c#l637).相比之下,[math.pow的源代码](http://hg.python.org/cpython/file/9a84cab7ff9d/Modules/mathmodule.c#l1780)确实将其参数首先转换为浮点数. (3认同)

Tom*_*rdt 12

Python的标准pow包括一个简单的黑客攻击pow(2, 3, 2)(2 ** 3) % 2(当然,你只会注意到大数字).

另一个很大的区别是两个函数如何处理不同的输入格式.

>>> pow(2, 1+0.5j)
(1.8810842093664877+0.679354250205337j)
>>> math.pow(2, 1+0.5j)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: can't convert complex to float
Run Code Online (Sandbox Code Playgroud)

但是,我不知道为什么有人宁愿math.powpow.