使用 metaclasses 隐式干预 python 中每个 class 的定义?

Intervene in definition of every class in python implicitly using metaclasses?

我一直在学习元classes,我想知道是否可以为 python 中定义的每个 class 添加一个新属性,而不仅仅是那些显式继承自自定义元 class.

我可以像这样使用自定义元class显式添加新属性

class NewAttrMeta(type):
    def __new__(cls, name, bases, attrs):
        attrs['new_attr'] = 'new_thing'
        return super().__new__(cls, name, bases, attrs)

class A(metaclass=NewAttrMeta):
   ...

print(A.new_attr)
$ 'new_thing'

但是是否可以在 每个 class 上强制进行这样的更改,而不仅仅是那些明确继承自您的自定义 metaclass?

我想也许因为所有 class 都是 type 类型,如果我用我的自定义元 class 覆盖 type 本身,那么所有新 classes 然后可能会继承它。由于 metaclasses 是 type 的子classes,那么所有以这种方式定义的 classes 仍然有效...

class NewAttrMeta(type):
    def __new__(cls, name, bases, attrs):
        attrs['new_attr'] = 'new_thing'
        return super().__new__(cls, name, bases, attrs)

type = NewMagicMeta

但这只有在 type 再次显式传入时才有效:

class A(type):
   ...

print(A.new_attr)
$ 'new_thing'

class B():
   ...

print(B.new_attr)
$ AttributeError: type object 'A' has no attribute 'new_attr'

我到底为什么要这样做?我想看看我是否可以通过覆盖每个 class 的 __getitem__ 方法在本地实现被拒绝的 PEP 472: "Support for indexing with keyword arguments" 的一个版本,该方法定义了 __getitem__ 的任何版本。我这样做只是为了好玩,所以我会对任何见解或替代方法感兴趣(黑客越多越好!)。

  1. Python 不允许修改内置声明的类型。那 意味着字典、列表、classes 定义在扩展中,例如 Numpy.ndarrays 来自 Python 代码的 "frozen"。

  2. 即使在可能的情况下,更改所有 classes 的 metaclass 也不会更改已经定义的 classes。所以,list,等等......不会受到影响。你可以安排你的程序,这样它可以 "install" 你的 class 创建钩子,然后再导入任何其他具有 class 定义的模块,但是 - 所以它可能会影响 classes 写在 Python代码。 (在扩展中创建的 类 是在 C 代码中定义的,并且无论如何都不通过元 class-class 创建过程)

  3. type 被引用为对象的元 class,因此即使您在内置函数中更改 type - 这是可能的,但不会自动用作任何人的 metaclass。默认情况下使用它,因为它是 type(object)

  4. 返回的内容

综上所述,可以创建一些东西来搜索 现有的 classes 在 运行 Python 程序中, 并且, 每当 class 在 Python 中定义时, 装饰一个 __getitem__ 方法, 如果它存在, 接受关键字参数。

但是然后:

  1. PEP 472 中提出的对索引参数的支持需要更改解析器和语言规范 - 仅接受 __getitem__ 中的关键字参数不会使 a[b=1]工作,或者不是语法错误。人们仍然必须写 a.__getitem__(b=1)

  2. __getitem__中的索引名称对于一种对象来说是非常具体的。 any class 在没有考虑到这一点的情况下设计是没有意义的。如果 a 是一个列表,那么 a[fish='golden'] 是什么意思?如果 a 是一个命令怎么办?

总而言之,如果您想出一些在索引中传递名称是有意义的东西,那么您已经拥有了一个非常酷的 class - 然后您可以使用任何方法检索它,并使用 a.get(fish="gold") 的常规括号表示法,或者甚至,如果您编写 __call__ 方法:a(fish="gold")