【问题标题】:Python Square Root for Class Instances类实例的 Python 平方根
【发布时间】:2019-05-08 07:31:56
【问题描述】:

我目前正在实现一个可以处理与物理单位相关的数字数据的类。

我想实现一种计算实例平方根的方法。假设您有一个具有属性值和名称的类的实例:

from math import sqrt

class Foo:
   def __init__(self, value, name)
      self.value = value
      self.name = name

   def __sqrt__(self):
      return sqrt(self.value)

我想实现一个类似于 add(self, other) 之类的魔术方法的函数,它会在我调用 math.sqrt() 函数时计算平方根:

A = Foo(4, "meter")
root = math.sqrt(A)

应该返回调用 A.sqrt() 函数。

【问题讨论】:

  • math.sqrt 只是许多其他函数之一,Python 显然不能为所有这些函数提供 dunder 方法
  • 我怀疑必须有 some 方法让 sqrt 接受自定义类实例。否则,math.sqrt(numpy.array([4])) 是如何工作的?
  • 之所以有效,是因为float(numpy.array([4])) 有效。如果您可以使用__float__ 返回一个值,那么 math.sqrt 将返回该值的平方根。
  • @wowserx,所以如果你实现了Foo.__float__,那么math.sqrt(Foo(4, "meter"))可以执行成功吗?这可能值得回答:-)
  • @Kevin 它真的不起作用。正如 wowserx 解释的那样,它只适用于具有单个元素的数组(因为它们可以转换为float)。用更大的数组试试,你会得到TypeError: only size-1 arrays can be converted to Python scalars

标签: python magic-methods


【解决方案1】:

您不能不将 math.sqrt 重新分配给自定义函数。如果你想让Foo 被转换为intfloat,你可以实现__int____float__ 并在调用math.sqrt 之前进行转换:

class Foo:
    def __init__(self, value, name)
        self.value = value
        self.name = name

    def __float__(self):
        return float(self.value)

    def __int__(self):
        return int(self.value)

A = Foo(4, "meter")
root = math.sqrt(float(A))

编辑:根据下面的 cmets,由于数学模块的实现方式,如果 Foo 实现 __float__,您似乎可以直接调用 math.sqrt(A)。我还是宁愿显式而不是隐式。

【讨论】:

  • 好主意。但您不必将A 转换为floatsqrt 会为你做到这一点
  • 请注意,math.sqrt 在某种程度上是一种特殊情况;由于math 包装了一个 C 库,它别无选择,只能将其接收到的 any Python 对象转换为可由底层 C 库处理的机器浮点数。通常,Python 函数不会尝试将参数转换为其他类型。
  • @DeepSpace 这种行为可以推广吗? CPython 的数学模块可能是这种情况,但其他 Python 工具链或其他数学模块和库可能不一定如此。
【解决方案2】:

没有简单/开箱即用的方式将math.sqrt 连接到Foo.__sqrt__

只需在Foo 中实现sqrt

class Foo:
     ...
     def sqrt(self):
          return sqrt(self.value)

A = Foo(4, "meter")
root = A.sqrt()

如果您出于某种原因坚持,它可能会被黑客入侵,但我看不出您有任何理由要这样做:

from math import sqrt

class Foo:
    def __init__(self, value, name):
        self.value = value
        self.name = name

    def __sqrt__(self):
        return sqrt(self.value)

orig_sqrt = sqrt

def my_sqrt(value):
    if isinstance(value, Foo):
        return orig_sqrt(value.value)
        # or return value.__sqrt__()
    else:
        return orig_sqrt(value)

sqrt = my_sqrt

A = Foo(4, "meter")
print(sqrt(A))
# 2.0
print(sqrt(4))
# 2.0

【讨论】:

  • 同意。我强烈建议在此答案的第一部分中编写幼稚的方法。
【解决方案3】:

sqrt 没有像其他数值函数那样的神奇方法。

你可以在你的类中定义一个sqrt 方法,正如另一个答案中所指出的那样。

您还可以创建自己的 sqrt 函数,如果传递了该类的实例,则该函数在您的类的属性上调用 math.sqrt

import math

def sqrt(x):
    if isinstance(x, Foo):
        return math.sqrt(x.value)
    return math.sqrt(x)

但是,这相当 hacky,除非您出于某种原因绝对需要在混合类型的容器上不加选择地调用 sqrt,否则不推荐使用它。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-12-23
    • 1970-01-01
    • 2013-06-10
    • 2015-06-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多