遍历类列表的Python包装器

2024-05-02 16:43:52 发布

您现在位置:Python中文网/ 问答频道 /正文

我使用的是一个开发良好的Python类(我们称之为Foo1D),它有一系列很好的方法,但只适用于1D数据。分叉回购和修改每个方法来处理二维数据是非常耗时的。你知道吗

因此,我想制作一个“矢量化”包装器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)

这将创建具有正确属性的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],[50, 52, 54, 56, 58]])时,我得到了array([40, 42, 44, 46, 48])。这是因为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]])。你知道吗

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


Tags: 方法inselffordatareturndefarray
1条回答
网友
1楼 · 发布于 2024-05-02 16:43:52

代码不起作用的原因有:

  1. 你想用__getattribute__而不是__getattr__。事实上Foo2D.__getattr__永远不会被调用(尝试在其中放入print语句)。你知道吗
  2. 如果固定点1,则__getattribute__将抛出错误RuntimeError: maximum recursion depth exceeded,因为self.data2D等价于self.__getattribute__('data2D')。你知道吗
  3. 我认为Foo2D.multiply是由self.__class__ = type(dummy.__class__.__name__, (self.__class__, dummy.__class__), {})设置的,所以你只得到Foo2D.data2D[0].multiply。我敢打赌这条线还有其他意想不到的后果。你知道吗
  4. 只有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的调用。你知道吗

相关问题 更多 >