我正在Python 3中编写一个小的线性代数模块,并且有许多二元运算符要定义。由于二元运算符的每个定义基本上是相同的,只有运算符本身发生了变化,我想通过只编写一次通用的二元运算符定义来保存一些工作。
例如:
class Vector(tuple):
def __new__(self, x):
super().__new__(x)
# Binary operators
def __add__(self, xs):
try:
return Vector(a + x for a, x in zip(self, xs)))
except:
return Vector(a + x for a in self)
def __and__(self, xs):
try:
return Vector(a & x for a, x in zip(self, xs))
except:
return Vector(a & x for a in self)
... # mul, div, or, sub, and all other binary operations
上面的二元运算符具有相同的形式。仅更改操作员。我想知道我是否可以立即编写所有操作符,如下所示:
def __bop__(self, xs):
bop = get_bop_somehow()
try:
return Vector(bop(a, x) for a, x in zip(self, xs)))
except:
return Vector(bop(a, x) for a in self)
我听说Python可以使用__getattr__
方法做一些神奇的事情,我试图用它来提取运算符的名称,如下所示:
def __getattr__(self, name):
print('Method name:', name.strip('_'))
但是,不幸的是,这只有在使用完整方法名称调用时才有效,而不是在使用运算符时。如何编写一个适合所有人的二元运算符定义?
答案 0 :(得分:2)
您可以使用类装饰器来改变您的类,并在工厂函数的帮助下将它们全部添加:
lib32ncurses5
还有其他一些方法可以做到这一点,但总体思路仍然相同。
答案 1 :(得分:1)
您可以使用operator
模块执行此操作,该模块为您提供操作符的功能版本。例如,operator.and_(a, b)
与a & b
相同。
因此return Vector(a + x for a in self)
变为return Vector(op(a, x) for a in self)
,您可以参数化op
。你仍然需要定义所有魔术方法,但它们可以是简单的传递方式。
答案 2 :(得分:1)
这可能超级慢,但您可以使用所有二进制方法创建一个抽象类并从中继承。
import operator
def binary_methods(cls):
operator_list = (
'__add__', '__sub__', '__mul__', '__truediv__',
'__floordiv__', '__and__', '__or__', '__xor__'
)
for name in operator_list:
bop = getattr(operator, name)
method = cls.__create_binary_method__(bop)
setattr(cls, name, method)
return cls
@binary_methods
class AbstractBinary:
@classmethod
def __create_binary_method__(cls, bop):
def binary_method(self, xs):
try:
return self.__class__(bop(a, x) for a, x in zip(self, xs))
except:
return self.__class__(bop(a, x) for a in self)
return binary_method
class Vector(AbstractBinary, tuple):
def __new__(self, x):
return super(self, Vector).__new__(Vector, x)
好的,我认为我有一个工作解决方案(仅在Python 2.X中测试),它使用类装饰器动态创建二进制方法。
import operator
def add_methods(cls):
operator_list = ('__add__', '__and__', '__mul__')
for name in operator_list:
func = getattr(operator, name)
# func needs to be a default argument to avoid the late-binding closure issue
method = lambda self, xs, func=func: cls.__bop__(self, func, xs)
setattr(cls, name, method)
return cls
@add_methods
class Vector(tuple):
def __new__(self, x):
return super(self, Vector).__new__(Vector, x)
def __bop__(self, bop, xs):
try:
return Vector(bop(a, x) for a, x in zip(self, xs))
except:
return Vector(bop(a, x) for a in self)
以下是一些示例用法:
v1 = Vector((1,2,3))
v2 = Vector((3,4,5))
print v1 * v2
# (3, 8, 15)
答案 3 :(得分:0)
你可以用__getattr__
做一些神奇的事情,但如果你能避免这样做,那么我会 - 它开始变得复杂!在这种情况下,你可能需要覆盖__getattribute__
,但请不要因为如果你开始搞乱__getattribute__
,你会咬自己的座位。< / p>
您可以通过简单地定义第一个,然后在其他函数中执行__and__ = __add__
,以非常简单的方式实现此目的。
class MyClass(object):
def comparison_1(self, thing):
return self is not thing
comparison_2 = comparison_1
A = MyClass()
print A.comparison_1(None)
print A.comparison_2(None)
print A.comparison_1(A)
print A.comparison_2(A)
给出
$ python tmp_x.py
True
True
False
False
但是,我不是这种hackery的粉丝。我会做的
class MyClass(object):
def comparison_1(self, thing):
"Compares this thing and another thing"
return self is not thing
def comparison_2(self, thing):
"compares this thing and another thing as well"
return self.comparison_1(thing)
为了清晰起见,最好写出额外的几行。
所以我用__getattribute__
尝试了它,不起作用:/。我承认我不知道为什么。
class MyClass(object):
def add(self, other):
print self, other
return None
def __getattribute__(self, attr):
if attr == '__add__':
attr = 'add'
return object.__getattribute__(self, attr)
X = MyClass()
print X.__add__
X + X
不能工作:/
andy@batman[18:15:12]:~$ p tmp_x.py
<bound method MyClass.add of <__main__.MyClass object at 0x7f52932ea450>>
Traceback (most recent call last):
File "tmp_x.py", line 15, in <module>
X + X
TypeError: unsupported operand type(s) for +: 'MyClass' and 'MyClass'