覆盖 python 中的所有二元运算符(或其他尊重物理维度的方式)?

Override all binary operators (or other way to respect physics dimensions) in python?

我正在构建 类 从 float 继承的方法,以在某些化学计算中考虑维度。例如:

class volume(float):
  def __init__(self, value):
    float._init_(value)

现在我的目标是:

  1. 在普通浮点数和 volume 实例之间使用 + 或 - 时引发错误
  2. return 一个卷实例,只要在两个卷实例之间使用 + 或 - 即可
  3. return 卷实例,无论何时使用 *(从两侧)和使用 /(从左侧)
  4. 从左侧使用 / 时引发错误
  5. return 每当两个体积实例被分割时一个浮点数。

现在,我将从左到右覆盖所有这四个运算符(例如 _add_ 和 _radd_);

err='Only objects of type volume can be added or subtracted together'
def __add__(self,volume2):
  if isinstance(volume2,volume): return volume(float(self)+volume2)
  else: raise TypeError(err)
def __radd__(self,volume2):
  if isinstance(volume2,volume): return volume(float(self)+volume2)
  else: raise TypeError(err)

有没有更简单的方法来访问所有这些,或者至少有一个表达式来包含运算符的左右用法?

看来这道题主要是为了避免代码重复。关于乘法和除法,您的功能略有不同,可能必须显式编写单独的方法,但对于加法和减法相关的方法,以下技术可行。它本质上是 monkey-patching class,可以这样做,尽管您不应该尝试类似于 monkey-patch instances in [=38] =] 3.

classVolume我按照惯例用大写V来命名

class Volume(float):
  pass

def _make_wrapper(name):
    def wrapper(self, other):
        if not isinstance(other, Volume):
            raise ValueError
        return Volume(getattr(float, name)(self, other))
    setattr(Volume, name, wrapper)

for _method in ('__add__', '__radd__', 
                '__sub__', '__rsub__',):                    
    _make_wrapper(_method)

这些情况下的等效显式方法如下所示,因此请根据乘法/除法情况的需要进行调整,但请注意显式使用 float.__add__(self, other) 而不是 self + other 正如问题所暗示的您打算使用的(问题提到 volume(self+volume2) 的地方),这将导致无限递归。

    def __add__(self, other):
        if not isinstance(other, Volume):
            raise ValueError
        return Volume(float.__add__(self, other))        

关于__init__,我现在已经在上面去掉了,因为如果它只是调用float.__init__,那么根本就不需要定义它(让它继承__init__ 来自基数 class)。如果你想有一个 __init__ 方法来初始化一些东西 else,那么是的你将 also 需要包含显式调用到 float.__init__ 就像你在问题中所做的那样(尽管注意双下划线 - 在你试图调用 float._init_ 的问题中)。

metaclass 是一种控制 class 构造方式的方法。

您可以使用 metaclasses 重载所有数学运算符,如下所示:

err='Only objects of type volume can be added or subtracted together'

class OverLoadMeta(type):
    def __new__(meta,name,bases,dct):
        # this is the operation you want to use instead of default add or subtract.
        def op(self,volume2):
            if isinstance(volume2,Volume): 
                return Volume(float.__add__(self,volume2))
            else:
                raise TypeError(err)
        # you can overload whatever method you want here
        for method in ('__add__','__radd__','__sub__'):
            dct[method] = op
        
        return super(OverLoadMeta, meta).__new__(meta, name, bases, dct)


class Volume(float,metaclass=OverLoadMeta):
    ""


# you can use it like this:
a = Volume(1)
b = Volume(2)

c = a+b
print(c.__class__)
# class will be <class '__main__.Volume'>

a + 1
# raise TypeError: Only objects of type volume can be added or subtracted together