2012-10-23 16 views
8

Ich möchte eine Liste aller möglichen Schlüsselwortargumente erhalten, die eine Zeichenfolgevorlage in einer Ersetzung verwenden könnte.Get keys from template

Gibt es eine Möglichkeit, dies anders als re?

mag ich so etwas wie dies zu tun:

text="$one is a $lonely $number." 
keys = get_keys(text) 
# keys = ('one', 'lonely', 'number') 

Ich bin ein einfaches Mad-lib-ähnliches Programm zu schreiben, und ich möchte mit entweder string.format oder Template strings Vorlage Substitution durchzuführen. Ich möchte die "Geschichte" schreiben und mein Programm eine Vorlagendatei aller "Schlüsselwörter" (Substantive, Verben usw.) erstellen lassen, die ein Benutzer produzieren müsste. Ich weiß, dass ich das mit regulären Ausdrücken machen kann, aber ich frage mich, ob es eine alternative Lösung gibt? Ich bin offen für Alternativen zu String.format und String-Vorlage.

Ich dachte, es wäre eine Lösung, aber ich habe es nicht in einer schnellen Suche gefunden. Ich habe diese Frage gefunden, reverse template with python, aber es ist nicht wirklich, was ich suche. Es bestätigt nur, dass dies mit re getan werden kann.

EDIT:

Ich sollte anmerken, dass $$ ist eine Flucht für '$', und ist kein Token ich will. $$5 sollte auf "$ 5" rendern.

Antwort

17

Wenn es in Ordnung ist string.format zu verwenden, sollten Sie mit integrierten Klasse string.Formatter, die eine parse() Methode hat:

>>> from string import Formatter 
>>> [i[1] for i in Formatter().parse('Hello {1} {foo}')] 
['1', 'foo'] 

here für mehr Details.

+0

So ziemlich, was ich gesucht habe. Vielleicht braucht meine Frage Arbeit, aber ich wollte das Rad nicht neu erfinden. Vielen Dank. – Yann

+0

Dies funktioniert nicht, wenn die Zeichenfolge mit einem Schlüssel beginnt. Versuchen Sie das oben genannte mit '{foo} test' – syntacticmarmalade

+0

@syntacticmarmalade Funktioniert gut für mich auf Python 3.6. Wenn Sie meinen, dass die Liste am Ende eine "None" hat, liegt das daran, dass die Zeichenfolge * nicht * mit einem Schlüssel endet. Sie können nur die Instanzen von 'None' filtern. –

1

versuchen str.strip() zusammen mit str.split():

In [54]: import string 

In [55]: text="$one is a $lonely $number." 

In [56]: [x.strip(string.punctuation) for x in text.split() if x.startswith("$")] 
Out[56]: ['one', 'lonely', 'number'] 
+0

** $ ** ist Teil ** string.punctuation **, was ** lstrip (‘ $ ') ** redundant – volcano

+0

@volcano gerade überprüft, Sie haben Recht. –

0

Sie könnten versuchen:

def get_keys(s): 
    tokens = filter(lambda x: x[0] == "$", s.split()) 
    return map(lambda x: x[1:], tokens) 
1

Warum Sie reguläre Ausdrücke vermeiden wollen? Sie arbeiten sehr gut dafür:

>>> re.findall(r'\$[a-z]+', "$one is a $lonely $number.") 
['$one', '$lonely', '$number'] 

Für Templating Besuche re.sub, kann es mit Rückruf aufgerufen werden fast das, was Sie tun wollen.

+0

Ich möchte keine regulären Ausdrücke vermeiden, ich habe mich nur gefragt, ob es einen anderen Weg gibt. – Yann

+0

Nun, wenn Sie ein afrikanisches Tier mit einem langen Hals wollen, ist es sicherlich möglich, ein Krokodil zu dehnen, aber in den meisten Fällen ist es einfacher mit einer Giraffe zu gehen. – che

0
>>> import string 
>>> get_keys = lambda s:[el.strip(string.punctuation) 
         for el in s.split()if el.startswith('$')] 
>>> get_keys("$one is a $lonely $number.") 
['one', 'lonely', 'number'] 
1

Sie könnten es einmal mit einem instrumentierten Wörterbuch rendern, das Aufrufe oder einen Standardbefehl aufzeichnet, und dann überprüfen, was es verlangt.

from collections import defaultdict 
d = defaultdict("bogus") 
text%d 
keys = d.keys() 
5

Die string.Template Klasse hat das Muster, das als ein Attribut ist, verwendet. Sie können das Muster drucken die passenden Gruppen

>>> print string.Template.pattern.pattern 

    \$(?: 
     (?P<escaped>\$) | # Escape sequence of two delimiters 
     (?P<named>[_a-z][_a-z0-9]*)  | # delimiter and a Python identifier 
     {(?P<braced>[_a-z][_a-z0-9]*)} | # delimiter and a braced identifier 
     (?P<invalid>)    # Other ill-formed delimiter exprs 
    ) 

Und für Ihr Beispiel zu bekommen,

>>> string.Template.pattern.findall("$one is a $lonely $number.") 
[('', 'one', '', ''), ('', 'lonely', '', ''), ('', 'number', '', '')]