问题描述
我正在构建从float继承的类,以在某些化学计算中尊重尺寸。例如:
class volume(float):
def __init__(self,value):
float._init_(value)
现在我的目标是:
- 在普通浮点数与volume实例之间使用+或-时引发错误
- 在两个卷实例之间使用+或-时,返回一个卷实例。
- 每当使用*(从两侧)和/(从左)时,返回卷的实例
- 从左开始使用/会引发错误
- 每当两个卷实例被分割时,返回浮点数。
现在,我将从左至右覆盖所有这四个运算符(例如_ 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,volume): return volume(float(self)+volume2)
else: raise TypeError(err)
是否有更简单的方法来访问所有这些对象,或者至少有一个表达式可以同时包含运算符的左右用法?
解决方法
似乎这个问题主要是关于避免代码重复。关于乘法和除法情况,您的功能稍有不同,可能必须显式地编写单独的方法,但是对于与加法和减法有关的方法,以下技术将起作用。本质上是用猴子修补类,并且可以这样做,尽管您不应该尝试与Python 3中的猴子修补 instances 类似。
按照惯例,我用大写V调用了Volume
类。
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,Volume):
raise ValueError
return Volume(float.__add__(self,other))
关于__init__
,我现在已经在上面删除了它,因为如果只需要调用float.__init__
,那么就根本不需要定义它(只需简单地继承{{1} })。如果您想使用__init__
方法来初始化 else ,那么是的,您 还需要包括对__init__
的显式调用就像您在问题中所做的一样(尽管请注意双下划线–在您试图致电float.__init__
的问题中)。
metaclass
是控制class
的构造方式。
您可以使用metaclasses
重载所有数学运算符,如下所示:
err='Only objects of type volume can be added or subtracted together'
class OverLoadMeta(type):
def __new__(meta,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__','__sub__'):
dct[method] = op
return super(OverLoadMeta,meta).__new__(meta,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