2

使用元类和__prepare__方法,可以在属性被添加到类的命名空间之前拦截它们。有没有办法,尽管它是 hacky,从类的范围内拦截裸表达式。

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?

4

3 回答 3

3

没有元类机制支持您尝试做的事情。1不会生成名称查找,因此__prepare__无济于事。哎呀,1作为一个声明本身根本不会生成任何字节码,因为它被优化了。

即使您愿意让元类定位类主体的函数对象并用检测过的字节码替换其字节码以尝试捕获这些表达式,它仍然不会在字节码中找到原始表达式的痕迹,并且将没有任何工具。此外,元类无法参与到类体的原始编译过程中。

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

于 2018-05-18T23:44:25.323 回答
1

它不是“干净的”,但它没有分配:

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)

于 2018-05-18T23:52:25.343 回答
0

你可以用类装饰器来做。例如:

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)

请注意,如果您希望被调用的元类方法能够对正在构造的类执行某些操作或以其他方式引用正在构造的类,则可以使用handle_expressions()a@classmethod而不是 a staticmethod

于 2018-05-19T00:10:57.157 回答