有没有办法以编程方式创建非常相似的 class 属性?

Is there a way to programmatically create very similar class properties?

我做了一个简单的 class 来接受一个表示数组形状的元组,并将名称分配给最后三个维度(深度、高度、宽度)作为 class 属性。我有兴趣将这些属性设置为属性,以便实例形状的任何更改都将反映在这些属性中。这会造成我有重复代码的情况,如果我决定在将来分配更多维度名称或 setter / 删除器,情况会变得更糟。

这是我的(精简版)class:


class Shape():
    """A class to represent 4-dimension names of a shape tuple"""
    def __init__(self, shape):
        """Shape instance is initialized with a shape tuple

        Args:
            shape (tuple): a tuple representing an array shape
        """
        self.shape = shape

    @property
    def shape(self):
        return self._shape

    @shape.setter
    def shape(self, value):
        self._shape = value

    # ndim is needed in case number of elements in shape is not equal to 3
    @property
    def ndim(self):
        return len(self.shape)

    # width, height, depth are similar
    @property
    def width(self):
        """Last dimension of shape"""
        if self.ndim >= 1:
            return self.shape[-1]

    @property
    def height(self):
        """Second last dimension of shape"""
        if self.ndim >= 2:
            return self.shape[-2]

    @property
    def depth(self):
        """Third last dimension of shape"""
        if self.ndim >= 3:
            return self.shape[-3]

实例化 class:

x = (4, 5)
shape1 = Shape(x)
print(shape1.shape)
print((shape1.depth, shape1.height, shape1.width))

(4, 5)
(None, 4, 5)

正在将形状属性设置为新值:

shape1.shape = (3, 2, 1)
print(shape1.shape)
print((shape1.depth, shape1.height, shape1.width))

(3, 2, 1)
(3, 2, 1)

所以我的 class 按预期工作,但是是否有更简洁的方法来在循环中设置多个相似的属性?我试图在 init 的循环中使用 setattr()。这适用于设置常规 class 属性,但我找不到用它设置属性的方法,这意味着属性将不再反映对实例形状的更新。

每个单独的 属性 都可以根据更通用的私有方法来实现。

class Shape:
    def __init__(self, shape):
        self.shape = shape

    def _get_dim(self, i):
        try:
            return self.shape[i]
        except IndexError:
            return None


    @property
    def shape(self):
        return self._shape

    @shape.setter
    def shape(self, value):
        self._shape = value

    @property
    def ndim(self):
        return len(self.shape)

    width = property(lambda self: self._get_dim(0))
    height = property(lambda self: self._get_dim(1))
    depth = property(lambda self: self._get_dim(2))

您可能更喜欢使用 operator.methodcaller 而不是 lambda 表达式,例如width = property(methodcaller("_get_dim", 0))


您可以更进一步,定义您自己的自定义描述符来代替 property

class Dimension:
    def __init__(self, n):
        self.n = n

    def __get__(self, obj, cls):
        if obj is None:
            return self
        try:
            return obj.shape[n]
        except IndexError:
            return None


class Shape:
    width = Dimension(0)
    height = Dimension(1)
    depth = Dimension(2)

    def __init__(self, shape):
        self.shape = shape

    @property
    def shape(self):
        return self._shape

    @shape.setter
    def shape(self, value):
        self._shape = value

    @property
    def ndim(self):
        return len(self.shape)