我正在使用一个完善的Python类(称为Foo1D
),该类具有很多很棒的方法,但仅适用于一维数据。分叉存储库并修改每种方法来处理2D数据非常耗时。
因此,我想制作一个“向量化”包装器Foo2D
,该包装器接受不相关的2D输入,将其转换为Foo1D
对象的集合,在调用方法时遍历该集合,并返回结果数组。
这是我尝试过的:
class Foo1D(object):
def __init__(self, data1D):
self.offset = 20
self.data = data1D+self.offset
def multiply(self, x):
return self.data*x
def add(self, a):
return self.data+a
class Foo2D(object):
def __init__(self, data2D):
dummy = Foo1D(data2D[0])
self.__class__ = type(dummy.__class__.__name__, (self.__class__, dummy.__class__), {})
self.__dict__ = dummy.__dict__
del dummy
self.data2D = [Foo1D(data1D) for data1D in data2D]
def __getattr__(self, item, *args, **kwargs):
result = [getattr(data1D, item)(*args, **kwargs) for data1D in self.data2D]
return np.array(result)
这将创建具有正确属性的
Foo1D
和Foo2D
对象,但是Foo2D
方法的行为不符合预期。一个例子:a1D = np.arange(10)
a2D = a1D.reshape(2,5)
A = Foo1D(a1D)
B = Foo2D(a2D)
因此,当我运行
A.multiply(2)
时,将得到预期的array([40, 42, 44, 46, 48, 50, 52, 54, 56, 58])
。但是,当我运行
B.multiply(2)
时,我希望得到array([40, 42, 44, 46, 48])
时得到array([[40, 42, 44, 46, 48],[50, 52, 54, 56, 58]])
。这是因为B.data
只是a2D
的第一个元素,但我不明白为什么。确实,如果我
np.array([getattr(i, 'multiply')(2) for i in B.data2D])
,我会得到我期望的array([[40, 42, 44, 46, 48],[50, 52, 54, 56, 58]])
答案。知道为什么会这样吗?谢谢!
最佳答案
您的代码无法正常工作的原因有几个:
您要使用__getattribute__
而不是__getattr__
。实际上,永远不会调用Foo2D.__getattr__
(尝试在其中放置打印语句)。
如果将点固定为1,则__getattribute__
将引发错误RuntimeError: maximum recursion depth exceeded
,因为self.data2D
等效于self.__getattribute__('data2D')
。
我认为Foo2D.multiply
是由self.__class__ = type(dummy.__class__.__name__, (self.__class__, dummy.__class__), {})
设置的,所以您只能得到Foo2D.data2D[0].multiply
。我愿意打赌这行还有其他意想不到的后果。
仅self
和属性名称传递给Foo2D.__getattribute__
。如果Foo2D.__getattr__
返回一个函数,则此函数将处理其他参数(例如*args, **kwargs
)。
下面我实现了Foo2D
的一个版本,我相信它可以达到预期的效果。 Foo2D.__getattribute__
尝试使用object.__getattribute__
,并且只有在抛出AttributeError
的情况下(即尚未设置该属性)才会做一些特殊的事情。如果Foo1D
中请求的属性是可调用的(即函数),则使用Foo2D._vec_attr
进行逐元素评估。否则,它仅给出属性的向量。
对于Python2:
class Foo2D(object):
def __init__(self, data2D):
self.data2D = [Foo1D(data1D) for data1D in data2D]
def __getattribute__(self, attr):
try:
return super(Foo2D, self).__getattribute__(attr)
except AttributeError:
if callable(getattr(self.data2D[0], attr)):
return lambda *args, **kwargs: self._vec_attr(attr, *args, **kwargs)
else:
return np.array([getattr(data1D, attr) for data1D in self.data2D])
def _vec_attr(self, attr, *args, **kwargs):
return np.array([getattr(data1D, attr)(*args, **kwargs) for data1D in self.data2D])
对于Python3:
class Foo2D(object):
def __init__(self, data2D):
self.data2D = [Foo1D(data1D) for data1D in data2D]
def __getattribute__(self, attr):
try:
return super().__getattribute__(attr)
except AttributeError:
if callable(getattr(self.data2D[0], attr)):
return lambda *args, **kwargs: self._vec_attr(attr, *args, **kwargs)
else:
return np.array([getattr(data1D, attr) for data1D in self.data2D])
def _vec_attr(self, attr, *args, **kwargs):
return np.array([getattr(data1D, attr)(*args, **kwargs) for data1D in self.data2D])
区别在于对
super
的调用。关于python - 遍历类列表的Python包装器,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/47912979/