python - 自动生成重载运算符?
问题描述
考虑以下在 Python 中具有所有向量操作的向量对象的实现:
import operator
class Vector:
def __init__(self, value):
self._vals = value.copy()
@classmethod
def _op(cls, this, that, oper, rev=False):
assert isinstance(this, cls)
if rev:
op = lambda a, b : oper(b, a)
else:
op = oper
if isinstance(that, list):
result = [op(x, y) for (x, y) in zip(this._vals, that)]
elif isinstance(that, cls):
result = [op(x, y) for (x, y) in zip(this._vals, that._vals)]
else:
# assume other is scalar
result = [op(x, that) for x in this._vals]
return cls(result)
def __add__(self, other):
return Vector._op(self, other, operator.add, False)
def __radd__(self, other):
return Vector._op(self, other, operator.add, True)
def __sub__(self, other):
return Vector._op(self, other, operator.sub, False)
def __rsub__(self, other):
return Vector._op(self, other, operator.sub, True)
def __mul__(self, other):
return Vector._op(self, other, operator.mul, False)
def __rmul__(self, other):
return Vector._op(self, other, operator.mul, True)
def __truediv__(self, other):
return Vector._op(self, other, operator.truediv, False)
def __rtruediv__(self, other):
return Vector._op(self, other, operator.truediv, True)
def __str__(self):
return str(self._vals)
很明显,所有重载的运算符 ( __add__
, __radd__
, ...) 都有完全相同的代码,除了传递给私有类方法的oper
and参数。 rev
_op
有没有办法避免所有的复制粘贴和自动(或半自动)这些操作?
解决方案
第一件事:类方法也可用于实例,因此您应该将方法编写为
def __someop__(self, other):
return self._op(self, other, ...)
或者,很容易获得一个对象的cls = type(obj)
类型_op
(
现在问您的问题:从技术上讲,您可以使用自定义元类或类装饰器来创建方法,即:
class VectorType(type):
def __new__(meta, name, bases, attrs):
todo = [
# name, operator, reverse
("__add__", operator.add, False),
("__sub__", operator.sub, False),
# etc
]
for name, oper, rev in todo:
# avoids overriding already defined methods
if name not in attrs:
def f(self, other):
return cls._op(self, other, oper=oper, rev=rev)
attrs[name] = f
return type.__new__(meta, name, bases, attr)
class Vector(metaclass=VectorType):
# etc
但这确实不会改善您现有的代码,除非您有很多(不相关的)类使用此方案,即使这样,普通的 mixin 类也可以正常工作。
如果您只有这个类的问题,那么您当前的代码是清晰、简单、易读和明确的,IOW 完全是 pythonic。
推荐阅读
- c# - Puppeteer-sharp:页面从 browser.NewPageAsync() 崩溃
- java - Java WS 获取 SOAP 请求 CDATA 正文
- c++ - std::forward 行为不同
- android - 尝试将图像添加到 Firebase 存储文件夹失败
- flutter - 发生了逃避。断言失败:'path.isNotEmpty' 文档路径必须是非空字符串
- python - matplotlib 以轴为单位设置 ylim
- dcmtk - DCMTK 存储 SCU TCP I/O 错误 - 现有连接被远程主机强行关闭。)发生在例程中:writeDataPDU
- c++ - 如何阅读此声明 int (*(*f2)(int n))[3];
- sql - 遵循连接:递归查询
- oauth-2.0 - 使用 PKCE 的 Next-Auth Okta 授权代码