metaclass 可以从 class' 范围操纵裸表达式吗?

Can a metaclass manipulate naked expressions from a class' scope?

使用 metaclass 和 __prepare__ 方法,可以在将属性添加到 class' 命名空间之前拦截它们。有没有办法,像它一样 hacky,从 class' 范围拦截裸表达式。

class Class(metaclass=Metaclass):
    # Would it be possible to intercept those:
    1
    2
    3

我发现允许类似的最接近语法是为此类表达式保留 _ 名称。

class ClassNamespace(dict):
    def __init__(self):
        self.expressions = []
        super().__init__()

    def __setitem__(self, key, value):
        if key == '_':
            self.expressions.append(value)
        else:
            super().__setitem__(key, value)

class Metaclass(type):
    def __prepare__(metacls, name):
        return ClassNamespace()

    def __new__(cls, name, bases, namespace):

        # Do something with the expressions
        print(namespace.expressions)

        return super().__new__(cls, name, bases, namespace)

class Class(metaclass=Metaclass):
    _ = 1
    _ = 2
    _ = 3

# prints: [1, 2, 3]

它是干净的,但是有没有办法在不使用赋值的情况下恢复 1、2 和 3?

没有元class 机制支持您正在尝试做的事情。 1 不会生成名称查找,因此 __prepare__ 没有帮助。见鬼,1 作为语句本身根本不会生成任何字节码,因为它被优化了。

即使您愿意让元class找到class主体的函数对象并将其字节码替换为检测字节码以尝试捕获这些表达式,它仍然找不到字节码中原始表达式的踪迹,并且没有任何可检测的内容。此外,metaclass 无法参与 class 正文的原始编译过程。

最合理(仍然非常疯狂)的选项似乎是让元class找到class的原始源代码,解析它,修改AST,重新编译并替换class 正文的原始代码对象。这永远不会在交互模式下工作(因为源代码不保存在交互模式下),这是一个坏主意的许多其他原因。

不是"clean",但是没有赋值:

class ClassNamespace(dict):
    def __init__(self):
        super().__init__()
        self.expressions = []
        self['record'] = self.expressions.append #make a special function to used for "clean" expressions

class Metaclass(type):
    def __prepare__(metacls, name):
        return ClassNamespace()

    def __new__(cls, name, bases, namespace):
        print(namespace.expressions)
        return super().__new__(cls, name, bases, namespace)

class Class(metaclass=Metaclass):
    record(1) #identifiers are first looked for in the class namespace, where we set "record" to append to the expressions list
    record(2)
    record(3)

您也可以将特殊功能设置为_:_(1)可能看起来比record(1)

更漂亮

您可以使用 class 装饰器来完成。例如:

class Metaclass(type):
    @staticmethod
    def handle_expressions(args):
        print(args)

def expressions(*args):
    """ Class decorator that passes expressions to Metaclass. """
    def decorator(cls):
        Metaclass.handle_expressions(args)  # Pass to metaclass method.
        return cls
    return decorator

#  Sample usage.
@expressions(1, 1+1, 2*2-1)
class Class(metaclass=Metaclass):
    pass

将打印:

(1, 2, 3)

请注意,如果您希望被调用的 metaclass 方法能够对正在构造的 class 执行某些操作,或者能够引用正在构建的 class,您可以将 handle_expressions() 设为 @classmethod 而不是 staticmethod.