Может ли метакласс управлять открытыми выражениями из области видимости класса? - PullRequest
0 голосов
/ 19 мая 2018

Метакласс и метод __prepare__ позволяют перехватывать атрибуты до их добавления в пространство имен класса.Есть ли способ, такой же хакерский, как и , перехватывать обнаженные выражения из области видимости класса.

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 без использования присваиваний?

Ответы [ 3 ]

0 голосов
/ 19 мая 2018

Это не «чисто», но без присваивания:

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)

0 голосов
/ 19 мая 2018

Вы можете сделать это с помощью декоратора классов.Например:

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 вместо staticmethod.

0 голосов
/ 19 мая 2018

Никакие механизмы метаклассов не поддерживают то, что вы пытаетесь сделать.1 не генерирует поиск имени, поэтому __prepare__ не помогает.Черт, 1 как сам по себе оператор не генерирует никакого байт-кода вообще, потому что он оптимизирован.

Даже если вы были готовы пойти так далеко, чтобы метакласс нашел объект функции длятело класса и заменяют свой байт-код на инструментированный байт-код, чтобы попытаться захватить эти выражения, он все равно не найдет никаких следов исходных выражений в байт-коде и не будет ничего для инструмента.Кроме того, у метакласса нет возможности принять участие в первоначальном этапе компиляции для тела класса.

Наиболее правдоподобный (все еще совершенно безумный) вариант, по-видимому, заключается в том, чтобы метакласс нашел исходный исходный код для класса., проанализируйте его, измените AST, перекомпилируйте и замените исходный объект кода для тела класса.Это никогда не будет работать в интерактивном режиме (так как исходный код не хранится в интерактивном режиме), среди многих других причин, почему это плохая идея.

...