【问题标题】:creating class object that normally looks like a float创建通常看起来像浮点数的类对象
【发布时间】:2014-04-19 17:31:27
【问题描述】:

我希望推出自己的简单对象,该对象可以跟踪变量的单位(也许我也会添加其他属性,例如公差)。这是我目前所拥有的:

class newVar():
    def __init__(self,value=0.0,units='unknown'):
        self.value=value
        self.units=units

    def __str__(self):
        return str(self.value) + '(' + self.units + ')'

    def __magicmethodIdontknow__(self): 
        return self.value


diameter=newVar(10.0,'m') #define diameter's value and units

print diameter #printing will print value followed by units

#intention is that I can still do ALL operations of the object
#and they will be performed on the self.value inside the object.

B=diameter*2 

因为我没有正确的魔术方法,所以我得到以下输出

10.0(m)
Traceback (most recent call last):
  File "C:\Users\user\workspace\pineCar\src\sandBox.py", line 25, in <module>
     B=diameter*2 
TypeError: unsupported operand type(s) for *: 'instance' and 'int'

我想我可以重写每个魔术方法来只返回 self.value 但这听起来是错误的。也许我需要一个装饰器?

另外,我知道我可以调用 diameter.value 但这似乎是重复的

【问题讨论】:

  • 您想通过对您的值进行操作来保留(和更新)您的单位吗?例如,newVar(10, "m") / newVar(5, "s") 是否应该给另一个newVar 实例,其单位为m / s
  • 你可能想在这里使用a library

标签: python magic-methods


【解决方案1】:

我曾经尝试自己实现类似的东西,但从未成功完成。一种方法是从头开始实现一个新类,其中包含值和单位。但是如果你想在计算中使用它,你必须实现所有魔术方法,如__add____mul__。另一种方法是子类 float 本身:

class FloatWithUnit(float):
    def __new__(cls, val, unit):
        return float.__new__(cls, val)
    def __init__(self, val, unit):
        self.unit = unit
    def __str__(self):  
        return '%g %s' % (self, self.unit)
    def __repr__(self):
        return self.__str__()

子类化 float 显然有点棘手,因此除了 __init__ 之外,您还必须实现 __new__,更多讨论请参见 here。在命令行输入这样的对象时,会显示其单位:

In [2]: g = FloatWithUnit(9.81, 'm/s^2')

In [3]: g
Out[3]: 9.81 m/s^2

In [4]: type(g)
Out[4]: __main__.FloatWithUnit

但是当用于计算时,它的行为就像一个普通的浮点数

In [5]: g2 = 2 * g

In [6]: g2
Out[6]: 19.62

In [7]: type(g2)
Out[7]: float

【讨论】:

  • 太棒了!这看起来像是我的问题的确切答案。我不完全理解为什么来自__new__ 的返回不能只是return val。看来我需要了解子类化
  • __new__ vs __init__ 的问题有点难。基本上,__new____init__ 之前被调用并且必须 返回一些对象。关于它在 SO 上的使用有一些很好的问题,你可以在网上找到各种教程,参见例如hereherehere。我必须承认我也不是很了解所有细节,我通常会复制一些示例并根据自己的需要进行修改。
【解决方案2】:
class newVar():

    def __init__(self,value=0.0,units='unknown'):
        self.value=value
        self.units=units

    def __repr__(self):
        return str(self.value) + '(' + self.units + ')'

    def __mul__(self, other):

        if hasattr(other, "value"):
            return self.value * other.value
        else:
            return self.value * other

diameter=newVar(10.0,'m')

print diameter 
print diameter*2

另一种可能的解决方案是覆盖对象浮点表示

class newVar():

    def __init__(self,value=0.0,units='unknown'):
        self.value=value
        self.units=units

    def __repr__(self):
        return str(self.value) + '(' + self.units + ')'

    def __float__(self):

        return self.value

diameter=newVar(10.0,'m')

print diameter 
print float(diameter)*2

【讨论】:

  • 我没有想过 __ float __ 但是,我希望能够只调用直径而无需修改或处理。如果没有其他方法,我只会在做数学时调用 diameter.value。
【解决方案3】:

在 Python 中,您可以覆盖许多不同的运算符,其中一些用于模拟数字类型。您可以了解更多here

在乘法的情况下,你需要做的就是像这样覆盖__mul__(self, other)

def __mul__(self, other):
    return self.value * other

您还可以覆盖其他数字运算,例如 __add__(self, other)(用于加法)、__sub__(self, other) 等。

在一些无关紧要的注释中,您可能还想考虑一下,如果您想将直径相乘或将权重添加到直径,您想要的行为是什么。

更新

根据您的评论,您可能最好重写 John 也提到的对象的浮点表示,尽管它不像重写数学运算符那样干净:

def __float__(self):
    return self.value

【讨论】:

  • 谢谢,我提到了。我计划对这个对象使用浮点数可用的所有操作(简单的乘法只是一个例子。)我不认为重写每个魔术方法是有意义的。我希望有一个单一的魔术方法可以覆盖所有数学运算,但似乎并非如此。
猜你喜欢
  • 1970-01-01
  • 2013-11-26
  • 1970-01-01
  • 1970-01-01
  • 2017-01-19
  • 1970-01-01
  • 1970-01-01
  • 2017-06-24
  • 1970-01-01
相关资源
最近更新 更多