2009-09-01 10 views
10

Mam grupę obiektów, dla których tworzę klasę, dla której chcę przechowywać każdy obiekt jako własny plik tekstowy. Naprawdę chciałbym zapisać go jako definicję klasy Python, która podklasuje główną klasę, którą tworzę. Zrobiłem trochę pokręcenia i znalazłem generator kodu Pythona na stronie effbot.org. Zrobiłem kilka eksperymentów z nim i oto co wymyśliłem:Python generujący python

# 
# 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') 

że czuje się naprawdę brzydki, jest tam czystsze/lepiej/bardziej pythonic sposób to zrobić? Proszę zauważyć, że nie jest to klasa, w której zamierzam to zrobić, jest to mała klasa, którą mogę z łatwością wykpić w niezbyt wielu liniach. Ponadto, podklasy nie muszą mieć w nich funkcji generującej, jeśli potrzebuję tego ponownie, mogę po prostu wywołać generator kodu z nadklasy.

+0

Nie używaj tabulatorów do wcięcia pytona, używaj spacji :) –

+0

tak, ustawia to jako domyślną, ale nadpisuję tę zmienną w/4 spacji. – Jonathanb

Odpowiedz

22

Używamy Jinja2 do wypełnienia szablonu. To znacznie prostsze.

Szablon wygląda trochę jak kod Pythona z kilkoma zamiennikami w nim {{something}}.

+0

Za mój komentarz do Anona, zamierzam po prostu użyć systemu szablonów. Będę się bacznie przyglądać Jinji, dzięki za napiwek! – Jonathanb

+2

jest na to przykład? – KJW

+0

To doskonały sposób na zrobienie tego! – specialscope

5

To jest prawie najlepszy sposób na wygenerowanie kodu Python ze źródła. Jednak można również wygenerować kod wykonywalny Pythona w środowisku wykonawczym przy użyciu biblioteki ast. (Łączę się z wersją Python 3, ponieważ jest bardziej wydajna niż wersja 2.x). Możesz zbudować kod używając drzewa składni abstrakcyjnej, a następnie przekazać je do compile(), aby skompilować je do kodu wykonywalnego. Następnie możesz użyć kodu eval.

Nie jestem pewien, czy istnieje wygodny sposób na zapisanie skompilowanego kodu do późniejszego wykorzystania (np. W pliku .pyc).

+0

cóż, naprawdę chcę coś czytelnego dla człowieka. Kod wykonywalny zwykle nie jest. – Jonathanb

+0

Dość uczciwi, zostawię tę odpowiedź tutaj, na wypadek, gdyby była przydatna dla kogoś z podobnym pytaniem. –

0

Z tego, co rozumiem, że próbujesz zrobić, rozważyłbym użycie refleksji, aby dynamicznie badać klasę w czasie wykonywania i generować na tej podstawie dane wyjściowe. Istnieje dobry samouczek dotyczący refleksji (introspekcja A.K.A.) pod adresem http://diveintopython3.ep.io/.

Możesz użyć funkcji dir(), aby uzyskać listę nazw atrybutów danego obiektu. Ciąg dokumentu obiektu jest dostępny za pośrednictwem atrybutu __doc__. Oznacza to, że jeśli chcesz spojrzeć na ciąg doc funkcji lub klasy można wykonać następujące czynności:

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

Mam pakiet planet, które chcę przechowywać jako własne pliki tekstowe. Nie jestem szczególnie przywiązany do przechowywania ich jako kodu źródłowego Pythona, ale jestem przywiązany do tego, aby były czytelne dla człowieka. – Jonathanb

1

Nie jestem pewien, czy jest to szczególnie pythonowy, ale można użyć przeciążanie operatorów:

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) 

Jest to oczywiście dużo droższe do wdrożenia niż twój przykład, a ja bym się obawiał nadmiaru meta-programowania, ale było to zabawne ćwiczenie.Możesz przedłużyć lub użyć tego, co uznasz za stosowne; Jak o:

  • dodając metodę zapisu i przekierowanie stdout do obiektu to drukowanie prosto do pliku skryptu
  • dziedziczy od niego na dostosowanie produkcji
  • dodawanie pobierające atrybutów i ustawiające

Byłoby wspaniale usłyszeć o co chcesz iść z :)

7

Wystarczy przeczytać komentarz do Wintermute - tj:

To, co mam, to garść planet, które chcę przechowywać jako własne pliki tekstowe . Nie jestem szczególnie przywiązany do do przechowywania ich jako kodu źródłowego Pythona, , ale jestem podłączony do ich tworzenia czytelny dla człowieka.

Jeśli tak jest, to wydaje się, że nie powinieneś potrzebować podklas, ale powinna być w stanie używać tej samej klasy i odróżnić planety od samych danych. A w takim przypadku, dlaczego nie wystarczy zapisać danych do plików, a kiedy potrzebujesz obiektów planety w swoim programie, odczytaj dane, aby zainicjować obiekty?

Jeśli potrzebowałeś czegoś takiego jak nadpisywanie metod, mógłbym zobaczyć napisany kod - ale czy nie powinieneś po prostu mieć te same metody dla wszystkich planet, używając tylko różnych zmiennych?

Zaleta właśnie zapisywania danych (może zawierać informację o typie etykiety dla czytelności, którą pominąłbyś po przeczytaniu), jest to, że programiści spoza Pythona nie będą się rozpraszać czytając je, możesz użyć te same pliki z innym językiem, jeśli to konieczne, itp.

+1

Tak, myślałem o tym zbyt mocno, nie ja. To jest prawdopodobnie to, co zrobię. Dziękuję Ci! – Jonathanb