Mah*_*ahé 5 python arrays numpy operator-overloading
在包含numpy数组作为属性的类中重载运算符时,我遇到了一个问题.根据操作数的顺序,结果类型将是我的类A(所需行为)或numpy数组.如何使它始终返回A的实例?
例:
import numpy as np
class A(object):
""" class overloading a numpy array for addition
"""
def __init__(self, values):
self.values = values
def __add__(self, x):
""" addition
"""
x = np.array(x) # make sure input is numpy compatible
return A(self.values + x)
def __radd__(self, x):
""" reversed-order (LHS <-> RHS) addition
"""
x = np.array(x) # make sure input is numpy compatible
return A(x + self.values)
def __array__(self):
""" so that numpy's array() returns values
"""
return self.values
def __repr__(self):
return "A object: "+repr(self.values)
Run Code Online (Sandbox Code Playgroud)
A的一个例子:
>>> a = A(np.arange(5))
Run Code Online (Sandbox Code Playgroud)
这按预期工作:
>>> a + np.ones(5)
A object: array([ 1., 2., 3., 4., 5.])
Run Code Online (Sandbox Code Playgroud)
这不是:
>>> np.ones(5) + a
array([ 1., 2., 3., 4., 5.])
Run Code Online (Sandbox Code Playgroud)
即使这很好:
>>> list(np.ones(5)) + a
A object: array([ 1., 2., 3., 4., 5.])
Run Code Online (Sandbox Code Playgroud)
第二个例子中发生的事情是根本不调用radd,而是调用__add__np.ones(5)中的numpy方法.
我从这篇文章中尝试了一些建议,但__array_priority__似乎没有任何区别(在seberg评论后编辑:至少在numpy 1.7.1中,但可能适用于较新版本),并__set_numeric_ops__导致Segmentation Fault ...我想我是做错了.
任何适用于上述简单示例的建议(同时保留__array__属性)?
编辑:我不希望A成为np.ndarray的子类,因为这将带来我想要避免的其他复杂性 - 至少现在.请注意,熊猫似乎解决了这个问题:
import pandas as pd
df = pd.DataFrame(np.arange(5))
type(df.values + df) is pd.DataFrame # returns True
isinstance(df, np.ndarray) # returns False
Run Code Online (Sandbox Code Playgroud)
我很想知道这是怎么做到的.
解决方案:除了子类化的M4rtini解决方案之外,还可以向__array_wrap__A类添加属性(以避免子类化).更多这里.根据塞伯格,__array_priority__也可以研究更新的numpy版本(见评论).
创建A一个子类,np.ndarrayPython将首先调用您的A.__radd__方法.
注意:如果右操作数的类型是左操作数类型的子类,并且该子类提供了操作的反射方法,则此方法将在左操作数的非反射方法之前调用.此行为允许子类覆盖其祖先的操作.
通过子类化您的A对象确实能够拦截添加:
>>> import numpy as np
>>> class A(np.ndarray):
... """ class overloading a numpy array for addition
... """
... def __init__(self, values):
... self.values = values
... def __add__(self, x):
... """ addition
... """
... x = np.array(x) # make sure input is numpy compatible
... return A(self.values + x)
... def __radd__(self, x):
... """ reversed-order (LHS <-> RHS) addition
... """
... x = np.array(x) # make sure input is numpy compatible
... return A(x + self.values)
... def __array__(self):
... """ so that numpy's array() returns values
... """
... return self.values
... def __repr__(self):
... return "A object: "+repr(self.values)
...
>>> a = A(np.arange(5))
>>> a + np.ones(5)
A object: array([ 1., 2., 3., 4., 5.])
>>> np.ones(5) + a
A object: array([ 1., 2., 3., 4., 5.])
Run Code Online (Sandbox Code Playgroud)
请研究Subclassing ndarray文档以获取警告和含义.
感谢@M4rtini和@seberg,添加似乎确实__array_wrap__解决了问题:
class A(object):
...
def __array_wrap__(self, result):
return A(result) # can add other attributes of self as constructor
Run Code Online (Sandbox Code Playgroud)
它似乎在任何 ufunc 操作结束时调用(包括数组加法)。这也是 pandas 的做法(在 0.12.0 中,pandas/core/frame.py l. 6020)。