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

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

# # a Python code generator backend # # fredrik lundh, march 1998 # # fredrik@pythonware.com # 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') 

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

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

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

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

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

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

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

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

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

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

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

Я не уверен, что это особенно 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 на объект этого для прямой печати в файл сценария
  • наследование от него для настройки вывода
  • добавление атрибутов getters и seters

Было бы здорово услышать о чем бы вы ни пошли 🙂

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

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

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