Python包装器迭代一个类列表

时间:2017-12-20 19:18:25

标签: python class

我正在使用一个开发良好的Python类(让它称之为Foo1D),它有很多很棒的方法,但只适用于1D数据。分割回购并修改每个方法来处理2D数据非常耗时。

所以,我想做一个"矢量化"接受不相关的2D输入的包装器Foo2D,将其转换为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)

这会使用正确的属性创建Foo1DFoo2D个对象,但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]])的答案。

知道为什么会这样吗?谢谢!

1 个答案:

答案 0 :(得分:1)

您的代码无效的原因有几个:

  1. 您想使用__getattribute__而不是__getattr__。事实上,Foo2D.__getattr__永远不会被调用(尝试在其中放置一个print语句)。
  2. 如果您修正了第1点,那么__getattribute__会抛出错误RuntimeError: maximum recursion depth exceeded,因为self.data2D相当于self.__getattribute__('data2D')
  3. 我认为Foo2D.multiplyself.__class__ = type(dummy.__class__.__name__, (self.__class__, dummy.__class__), {})设置,因此您只能获得Foo2D.data2D[0].multiply。我愿意打赌这条线有其他意想不到的后果。
  4. self和属性名称传递给Foo2D.__getattribute__。如果Foo2D.__getattr__返回一个函数,则此函数处理其他参数(例如*args, **kwargs)。
  5. 下面我已经实现了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的调用。