Python - 在对象实例中使用元class & class 属性

Python - using metaclass & class attributes in objec instances

我有许多 classes 继承了一个共同的。我需要父 class 来跟踪在 class 级别定义的一堆 dependancies/relationships。类似于:

class Meta(type):
    ALLDEPENDANCIES = {}
    def __new__(meta, name, bases, attrs):
        if "DEPENDANCIES" in attrs.keys():
            for key, value in attrs.items():
                if key == "DEPENDANCIES":
                    meta.ALLDEPENDANCIES.update(attrs["DEPENDANCIES"])
        return type.__new__(meta, name, bases, attrs)

class DataTable(DataFrameWrapper, metaclass=Meta):
    pass


class Foo(DataTable):
    DEPENDANCIES = {"a":1}


class Bar(DataTable):
    DEPENDANCIES = {"b":2}

基本上,当我创建新的 classes(Foo、Bar、Baz...)时,它们每个都有一个字典。我需要合并每个词典中的信息。所以我正在使用 metaclass,如上所示。每个 class 作为一个 DEPENDANCIES 属性,我将所有这些收集到 metaclass.

中定义的 ALLDEPENDANCIES 属性中

如果我这样做,它似乎工作正常:

import Foo, Bar
print(Foo.ALLDEPENDANCIES)
>> {"a":1, "b":2}
print(Bar.ALLDEPENDANCIES)
>> {"a":1, "b":2}

但是,在处理 if obj 实例时,缺少 ALLDEPENDANCIES 属性:

f = Foo()
b = Bar()
print(f.ALLDEPENDANCIES)
print(b.ALLDEPENDANCIES)

属性错误 - 没有 ALLDEPENDANCIES。

我认为元class 中定义的class 属性可以从实例中的self.myattribute 访问,就像DEPENDANCIES 一样。我做错了什么?

Meta 描述 how 来创建 class 但不是 what class 那将是。

Meta != Parent 具有继承属性

所以你必须将适当的属性传递给新的 class:

class Meta(type):
    _a = {}
    def __new__(meta, name, bases, attrs):
        if "d" in attrs:
            meta._a.update(attrs["d"])
        attrs["a"] = meta._a
        return type.__new__(meta, name, bases, attrs)

class Data:
    pass

class DataTable(Data, metaclass=Meta):
    pass

class Foo(DataTable):
    d = {"a":1}

class Bar(DataTable):
    d = {"b":2}

f = Foo()
print(Foo.a)
print(f.a)
{'a': 1, 'b': 2}
{'a': 1, 'b': 2}

实例 class 属性搜索不进入元 class - 仅进入 class。 metaclass 可以在每个新 class 中设置 ALLDEPENDANCIES,在其 __new__ 中有一行,但如果你想要更清晰的代码,在某种意义上字典没有别名在任何地方,您都可以通过 class.

访问属性

按原样使用您的代码:

Foo().__class__.ALLDEPENDANCIES  

将在任何地方工作(就像 `type(Foo()).ALLDEPENDANCIES)。

为了在新的classes中设置该属性,使其在新创建的classes中可见,一个选项是:


from types import MappingProxyType

class Meta(type):
    ALLDEPENDANCIES = {}
    ALLDEPSVIEW = MappingProxyType(ALLDEPENDANCIES)
    def __new__(meta, name, bases, attrs):
        if "DEPENDANCIES" in attrs.keys():
            for key, value in attrs.items():
                if key == "DEPENDANCIES":
                    meta.ALLDEPENDANCIES.update(attrs["DEPENDANCIES"])
        new_cls = super().__new__(meta, name, bases, attrs)
        new_cls.ALLDEPENDANCIES = meta.ALLDEPSVIEW
        return new_cls

(在调用 type.__new__ 之前在 attrs 中插入新的 attr 也可以)

这里我做了另外两个额外的事情:(1) 调用 super().__new__ 而不是对 type.__new__ 的硬编码调用:这允许你的 metaclass 可以与其他 meta[= 组合31=]es,如果你的一个 classes 将与其他 metaclass 交叉(例如,如果你使用来自 [=17= 的抽象基础 classes ] 或 collections.abc)。并且 (2) 使用 MappingProxyType,它是一个“只读”字典视图,将停止通过 classes 或实例对字典进行酸性直接更新。