Python, генерирующий Python

У меня есть группа объектов, для которых я создаю класс, для которого я хочу сохранить каждый объект в виде собственного текстового файла. Я действительно хотел бы сохранить его как определение класса Python, которое подклассифицирует основной класс, который я создаю. Итак, я немного пошутил и нашел генератор кода Python на effbot.org. Я немного экспериментировал с этим, и вот что я придумал:

#
# a Python code generator backend
#
# fredrik lundh, march 1998
#
# [email protected]
# http://www.pythonware.com
#
# Code taken from http://effbot.org/zone/python-code-generator.htm

import sys, string

class CodeGeneratorBackend:

    def begin(self, tab="\t"):
        self.code = []
        self.tab = tab
        self.level = 0

    def end(self):
        return string.join(self.code, "")

    def write(self, string):
        self.code.append(self.tab * self.level + string)

    def indent(self):
        self.level = self.level + 1

    def dedent(self):
        if self.level == 0:
            raise SyntaxError, "internal error in code generator"
        self.level = self.level - 1

class Point():
    """Defines a Point. Has x and y."""
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def dump_self(self, filename):
        self.c = CodeGeneratorBackend()
        self.c.begin(tab="    ")
        self.c.write("class {0}{1}Point()\n".format(self.x,self.y))
        self.c.indent()
        self.c.write('"""Defines a Point. Has x and y"""\n')
        self.c.write('def __init__(self, x={0}, y={1}):\n'.format(self.x, self.y))
        self.c.indent()
        self.c.write('self.x = {0}\n'.format(self.x))
        self.c.write('self.y = {0}\n'.format(self.y))
        self.c.dedent()
        self.c.dedent()
        f = open(filename,'w')
        f.write(self.c.end())
        f.close()

if __name__ == "__main__":
    p = Point(3,4)
    p.dump_self('demo.py')

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

Ответ 1

Мы используем Jinja2 для заполнения шаблона. Это намного проще.

Шаблон очень похож на код Python с несколькими заменами {{something}}.

Ответ 2

Просто прочитайте свой комментарий к wintermute - ie:

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

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

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

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

Ответ 3

Это в значительной степени лучший способ генерации исходного кода Python. Однако вы также можете генерировать исполняемый код Python во время выполнения, используя библиотеку ast. (Я ссылался на версию Python 3, потому что она более способна, чем версия 2.x.) Вы можете создать код, используя абстрактное синтаксическое дерево, а затем передать его в compile() чтобы скомпилировать его в исполняемый код. Затем вы можете использовать eval для запуска кода.

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

Ответ 4

Я бы рекомендовал использовать cookiecutter для генерации кода.

Ответ 5

Я не уверен, что это особенно Pythonic, но вы можете использовать перегрузку оператора:

class CodeGenerator:
    def __init__(self, indentation='\t'):
        self.indentation = indentation
        self.level = 0
        self.code = ''

    def indent(self):
        self.level += 1

    def dedent(self):
        if self.level > 0:
            self.level -= 1

    def __add__(self, value):
        temp = CodeGenerator(indentation=self.indentation)
        temp.level = self.level
        temp.code = str(self) + ''.join([self.indentation for i in range(0, self.level)]) + str(value)
        return temp

    def __str__(self):
        return str(self.code)

a = CodeGenerator()
a += 'for a in range(1, 3):\n'
a.indent()
a += 'for b in range(4, 6):\n'
a.indent()
a += 'print(a * b)\n'
a.dedent()
a += '# pointless comment\n'
print(a)

Это, конечно, намного дороже, чем ваш пример, и я бы опасался слишком большого метапрограммирования, но это было весело. Вы можете расширять или использовать это, как вы сочтете нужным; как насчет:

  • добавление метода записи и перенаправление stdout на объект этого для прямой печати в файл script
  • наследует от него, чтобы настроить вывод
  • добавление атрибутов getters и seters

Было бы здорово услышать о том, с чем вы столкнулись:)

Ответ 6

Из того, что, как я понимаю, вы пытаетесь сделать, я бы подумал об использовании рефлексии для динамического изучения класса во время выполнения и генерации вывода на основе этого. Существует хороший учебник по отражению (интроспекция А.К.А.) на http://diveintopython3.ep.io/.

Вы можете использовать функцию dir(), чтобы получить список имен атрибутов данного объекта. Строка doc объекта доступна через атрибут __doc__. То есть, если вы хотите посмотреть на строку документа функции или класса, вы можете сделать следующее:

>>> def foo():
...    """A doc string comment."""
...    pass
...
>>> print foo.__doc__
A doc string comment.

Ответ 7

У меня был очень хороший опыт работы с Red Baron при создании кода.

Он прост в использовании и очень прост в реализации.