在 Python 中,是否可以接受使用元 class 添加和删除许多 class 属性?

In Python, is it acceptable to use a metaclass to add and remove many class attributes?

具体来说,是acceptable/good的做法是使用一个class属性创建更多的class属性,然后删除原来的?

说我有一个超级class。由此,我派生出许多子classes,但所有这些都需要定义一些class 属性。如果我正常执行此操作,它将看起来像:

class Bar(Foo):
    someval = 0
    someotherval = ('a', 'b', 'c')
    importantval = [1, 2, 3]
    thisval = 'this'
    names = ['John', 'Joe']

每个子class 都需要定义所有这些属性。但是,如果我们以某种方式使用一个变量来创建所有这些,它将看起来像:

class Bar(Foo):
    classvars = (0, ('a', 'b', 'c'), [1, 2, 3], 'this', ['John', 'Joe'])

然后,在创建 class 之后,它在内部看起来与我们单独定义所有属性的 class 版本相同。

看,我之前尝试通过仅使用 superclass 而没有 metaclass 来做到这一点,例如:

class Foo:
    @classmethod
    def __init__(cls, val):
        cls.val = val

class Bar(Foo):
    Foo.__init__(5)

但是如果您知道 @classmethod 是如何工作的,那么您就会知道 cls 最终会成为 class Foo 引用,而不是 class Bar 引用(在 Bar 创建期间调用时,cls 最终仅在 Bar 实例创建期间成为 Bar 引用)。然后我也尝试使用 @staticmethod 代替,但是据我所知,在创建 class 时,您不能引用在方法定义之外创建的 class。

class Bar(Foo):
    Foo.__init__(Bar, 5)

这会引发 NameError(如果 Foo__init__ 方法是静态方法)。

最终,我了解了 metaclasses,我发现我可以做到这一点:

class MetaFoo(type):
    def __init__(cls, name, bases, namespace):
        super(MetaFoo, cls).__init__(name, bases, namespace)
        if '_superclass' not in namespace:  # A special attribute to distinguish between classes that we want to affect and those that we don't
            attrnames = ('someval', 'someotherval', 'importantval', 'thisval', 'names')
            if 'classvars' in namespace:
                for attr, attrname in zip(getattr(cls, 'classvars'), attrnames):
                    setattr(cls, attrname, attr)
                    namespace[attrname] = attr
                delattr(cls, 'classvars')
            else:  # Allow the definitions to be separate, instead of through "classvars", but also make sure that all the required attributes are defined
                for attrname in attrnames:
                    if attrname not in namespace:
                        raise AttributeError('"%s" not found.' % attrname)


class Foo(metaclass=MetaFoo):
    _superclass = True  # The value of this attribute doesn't matter

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

    def dosomething(self):
        return self.value * self.someval  # Can use type(self).someval for the class attribute, as long as the class itself is never passed as self


class Bar(Foo):
    classvars = (5, ('c', 'b', 'a'), [3, 2, 1], 'This', ['Jimmy', 'Bob'])

上面所做的,简而言之,如果它找到一个名为 classvars 的 class 属性,它会使用它来创建其他 class 属性,然后它会删除 classvars.在这个例子中,做:

class Bar(Foo):
    classvars = (5, ('c', 'b', 'a'), [3, 2, 1], 'This', ['Jimmy', 'Bob'])

给你一个相同的结果:

class Bar(Foo):
    someval = 5
    someotherval = ('c', 'b', 'a')
    importantval = [3, 2, 1]
    thisval = 'This'
    names = ['Jimmy', 'Bob']

我的主要问题是:以这种方式使用 metaclass 是否可以接受(或者出于某种原因通常会避免),特别是如果您已经在使用 metaclass有充分的理由吗?

附带的问题是:没有元class有什么方法可以做到这一点吗?

是的,你可以做到。但我不知道它是否比复制+粘贴所有需要的属性更好。

如果您要这样做,为了让查看您的子class之一的代码的人保持最低限度的可读性 - 包括从现在起的一周内的您 - 您至少应该使用字典而不是序列。这样,在查看子 class.

时,您将始终知道哪个值映射到每个属性

此外,请记住,如果至少某些属性具有对许多子classes 有效的良好默认值,则继承机制可以很好地保留这些默认值,同时允许您只明确声明每个子 class 真正需要不同的属性(并且输入比使用字典更简单,这至少需要在名称周围加上额外的引号)。

否则,就语言而言,还可以。您宁愿在基础 class 上有一个特殊的属性名称,允许您在基础 class 主体上输入 required/desired 属性名称,而不是将它们硬编码在元 [=26] 上=]:看起来真的 "uncivilized"。通过在 baseclass 上添加一个属性,metaclass 可以在不同的对象层次结构中重复使用。

(只需使用 forbases 参数迭代到元 class 以检查是否存在具有属性名称的参数)

啊,因为它是问题的一部分:关于删除 class 创建 class 后不再有用的属性,使用 metaclass - 完全可以.