在Python中,可以使用元类添加和删除许多类属性吗?

在Python中,可以使用元类添加和删除许多类属性吗?,python,python-3.x,class,inheritance,metaclass,Python,Python 3.x,Class,Inheritance,Metaclass,具体来说,使用一个类属性创建更多的类属性,然后删除原始属性,这是否可以接受/良好的做法 假设我有一个超类。由此,我派生了许多子类,但所有这些都需要定义一些类属性。如果我正常地这样做,它看起来会像: class Bar(Foo): someval = 0 someotherval = ('a', 'b', 'c') importantval = [1, 2, 3] thisval = 'this' names = ['John', 'Joe'] class

具体来说,使用一个类属性创建更多的类属性,然后删除原始属性,这是否可以接受/良好的做法

假设我有一个超类。由此,我派生了许多子类,但所有这些都需要定义一些类属性。如果我正常地这样做,它看起来会像:

class Bar(Foo):
    someval = 0
    someotherval = ('a', 'b', 'c')
    importantval = [1, 2, 3]
    thisval = 'this'
    names = ['John', 'Joe']
class Bar(Foo):
    classvars = (0, ('a', 'b', 'c'), [1, 2, 3], 'this', ['John', 'Joe'])
class Foo:
    @classmethod
    def __init__(cls, val):
        cls.val = val

class Bar(Foo):
    Foo.__init__(5)
每个子类都需要定义所有这些属性。然而,如果我们以某种方式使用一个变量来创建所有这些变量,它将看起来像:

class Bar(Foo):
    someval = 0
    someotherval = ('a', 'b', 'c')
    importantval = [1, 2, 3]
    thisval = 'this'
    names = ['John', 'Joe']
class Bar(Foo):
    classvars = (0, ('a', 'b', 'c'), [1, 2, 3], 'this', ['John', 'Joe'])
class Foo:
    @classmethod
    def __init__(cls, val):
        cls.val = val

class Bar(Foo):
    Foo.__init__(5)
然后,在创建类之后,它的内部外观将与我们单独定义所有属性的类的版本相同

看,我以前尝试过这样做,只使用一个超类,而不使用元类,比如:

class Bar(Foo):
    someval = 0
    someotherval = ('a', 'b', 'c')
    importantval = [1, 2, 3]
    thisval = 'this'
    names = ['John', 'Joe']
class Bar(Foo):
    classvars = (0, ('a', 'b', 'c'), [1, 2, 3], 'this', ['John', 'Joe'])
class Foo:
    @classmethod
    def __init__(cls, val):
        cls.val = val

class Bar(Foo):
    Foo.__init__(5)
但是如果您知道
@classmethod
是如何工作的,那么您就知道
cls
最终是一个类
Foo
引用,而不是一个类
Bar
引用(当在
Bar
创建过程中调用时,
cls
仅在
Bar
实例创建过程中是一个
Bar
引用). 然后,我还尝试使用
@staticmethod
,但据我所知,在创建类时,不能引用在方法定义之外创建的类

i、 e

如果
Foo
\uuuu init\uuuu
方法是一个静态方法,那么这将引发一个名称错误

最后,我学习了元类,我发现我可以做到:

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
的类属性,它将使用该属性创建其他类属性,然后删除
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']
我的主要问题是:以这种方式使用元类是可以接受的(或者通常是因为某种原因而避免的),特别是如果您已经在使用元类有很好的理由的话


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

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

如果你愿意这样做,为了让一个人在看你的一个子类的代码时保持最小的可读性——这包括从现在起一周后的你——你至少应该使用字典而不是序列。这样,在查看子类时,您将始终知道哪些值映射到每个属性

另外,请记住,如果至少有一些属性具有良好的默认值,这些默认值对许多子类都是有效的,那么继承机制在保留这些默认值的同时,只允许显式声明每个子类真正需要不同的属性(与使用字典相比,键入的内容要少一些,因为字典至少需要在名称周围加上引号)

否则,就语言而言,这是可以的。您希望在基类上有一个特殊的属性名称,允许您在基类主体上输入所需/所需的属性名称,而不是在元类上硬编码:这看起来真的“不文明”。通过在基类上具有属性,该元类可用于不同的对象层次结构

(只需使用
for
对元类的
base
参数进行迭代,以检查是否存在具有属性名称的参数)


啊,因为这是问题的一部分:关于删除类创建后不有用的类属性,使用元类-这完全可以。

我不会这么做,因为“显式优于隐式”。@cco我知道这是关于编码样式本身的,但如果你说得清楚怎么办(通过代码/文档中的注释)创建
Bar
的两种风格是相同的,只是其中一种更快、更高效。如果你有10个不同的类变量需要定义,25个不同的子类需要创建,那么你至少需要275行代码才能正常创建,而短时间内只有50行代码。我同意你的说法r、 但我怀疑它是否更快(跑,而不是写)或者更有效。对我来说,将名称与值分离是一个重大的损失;不同的意见同样有效。在另一种方法上,你可以使用类装饰器,这将使超类不必要跳舞,并使魔法不那么隐藏(一件好事,IMHO).您是在运行时创建类吗?您是在自动创建模块吗?您想解决什么问题?-出于您的目的,为什么具有单个
classvars
属性的子类定义比具有所有类属性的子类定义更好?@cco我的意思是,它在编写代码时会更高效e:不用花20分钟来编写所有的子类,可能只需要2分钟。我甚至不知道如何使用类装饰器来完成这项工作,但我知道如何使用元类来完成(这其实很简单),这就是我这样做的原因。就继承而言,我实际情况中的每个属性都必须特定于子类,它们不能有默认值,或者没有意义,因为它们特定于子类所表示的内容。使用字典几乎无法消除重复名称和属性的问题同时键入值。为属性名设置超类属性是一个好主意,但我没有这样做,因为在我的情况下,所需的特定属性根本不可能更改(另外,如果需要,我将只对名称“提示”使用注释).总的来说,答案是好的,有帮助的。正如我所说的:不,没有什么反对它的-主要缺点是我概述的-但是没有什么是带有参数顺序的注释行不能解决的。