Перепишите скрипт на Python, внедрив метод в каждый из классов скрипта

#python #code-generation

#python #генерация кода

Вопрос:

Допустим, у меня есть модуль python foo.py который содержит:

 class Foo(object):
    def __init__(self):
        pass
  

Затем я хочу разобрать этот скрипт и внедрить метод в каждый из его классов, переписав его примерно так:

 class Foo(object):
    def __init__(self):
        pass
    def my_method(self):
        pass # do some stuff here
  

Я заметил, что в python 2.6 есть ast модуль, который можно было бы использовать для этого, но, к сожалению, мне нужно сделать это в python 2.5. Приветствуются любые предложения.

Комментарии:

1. Вы на самом деле анализируете исходные текстовые файлы Python .py и хотите записать . py-файлы, или вы говорите об изменении определений классов в памяти времени выполнения?

2. @dkamins разбирает исходные текстовые файлы Python .py и хочет записать. py-файлы

Ответ №1:

Я понимаю, что вы пытаетесь сделать, но если ваш исходный код достаточно прост, возможно, вы могли бы вообще пропустить весь синтаксический анализ и обратную запись и просто запустить регулярное выражение?

Что-то вроде:

 re.sub('^classs (w ):s*n', 'class \1:n%s' % method_source, file_source, flags=re.M)
  

Просто сделайте правильный отступ..

Комментарии:

1. Пример приведен просто для того, чтобы указать на то, чего я пытаюсь достичь. Фактический исходный код, который я пытаюсь отредактировать, представляет собой гораздо более сложный скрипт, поэтому я бы не стал полагаться на простой синтаксический анализ строки. Например, посмотрев на ваше регулярное выражение, я мог бы использовать слово ‘class’ как часть многострочного комментария, и в этом случае регулярное выражение завершилось бы ошибкой

2. На самом деле существует не так много способов для сбоя. Вложенные классы не будут исправлены. У вас есть какой-нибудь? Должны ли они быть исправлены? Все, что произойдет с многострочным комментарием, окружающим класс, — это то, что этот класс тоже будет отредактирован. Если вам нужен реальный анализатор, вы могли бы использовать pyparsing и это грамматическое определение: pyparsing. wikispaces.com/file/view/pythonGrammarParser.py

3. Регулярное выражение не будет работать для классов нового стиля. И даже если вы это исправите, у вас может возникнуть ситуация, когда унаследованные классы распределяются по нескольким строкам. Вы могли бы исправить и это, но тогда результирующее регулярное выражение рисковало бы превратиться в чудовище.

Ответ №2:

У вас уже есть скрипт? Вы должны быть в состоянии исправить его прямо сейчас. Чтобы удовлетворить ваши текущие требования, было бы неплохо что-то вроде этого:

 class Foo(object):
    def my_method(self):
        pass
  

В качестве альтернативы, вы могли бы определить my method и добавить его в Foo класс в качестве атрибута:

 def my_method(self):
    pass

Foo.my_method = my_method
#or
Foo.__dict__.my_method = my_method
  

Комментарии:

1. Я думаю, что он хочет добавлять их динамически во время выполнения и без указания имени каждого класса. Что-то вроде for spam in foo.classes: spam.my_method = my_method .

2. Я думал о втором подходе. Это не будет работать так прямолинейно, как в вашем примере, но это будет работать при использовании нового модуля python ( docs.python.org/library/new.html ). Проблема в том, что сгенерированный скрипт будет довольно сложным для чтения, поэтому я бы предпочел что-то вроде вашего первого предложения, но я не знаю, как безопасно это сделать, не прибегая к построению синтаксического дерева, добавлению к нему узлов, а затем его перезаписи…

3. @Ioan вы должны добавить ответ на свой вопрос и позволить другим проголосовать за него. Это звучит как разумный подход.

4. @Ioan Если вы перепишете все свое синтаксическое дерево, вы можете потерять комментарии (которые не включены в стандартный анализатор Python) и форматирование.

5. @Gurgeh Да, меня на самом деле не волнует потеря комментариев. Меня беспокоит только то, чтобы выходной скрипт функционировал в ожидаемом режиме.

Ответ №3:

Решением было бы:

 class Foo(object):
    def __init__(self):
        pass

import new

def some_func(self):
    print "foobar"

Foo.my_method = new.instancemethod(some_func, None, Foo)
  

Проблема в том, что мне не нравится добавлять определения нового метода в конец файла. Я бы хотел, чтобы классы в сгенерированном скрипте выглядели следующим образом:

 class Foo(object):
    def __init__(self):
        pass
    def my_method(self):
        print "foobar"
  

но не могу найти решение для этого без построения, модификации и последующей обратной записи дерева синтаксиса.