Obtener claves de plantilla

Me gustaría obtener una lista de todos los posibles argumentos de palabras clave que una plantilla de cadena podría usar en una sustitución.

¿Hay una manera de hacer esto que no sea re?

Quiero hacer algo como esto:

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

Estoy escribiendo un simple progtwig similar a Mad-lib, y quiero realizar la sustitución de plantillas con string.format o Template Strings . Me gustaría escribir la ‘historia’ y hacer que mi progtwig produzca un archivo de plantilla con todas las ‘palabras clave’ (sustantivos, verbos, etc.) que un usuario debería producir. Sé que puedo hacer esto con expresiones regulares, pero me preguntaba si hay una solución alternativa. Estoy abierto a alternativas a string.format y string template.

Pensé que habría una solución para esto, pero no lo he encontrado en una búsqueda rápida. Encontré esta pregunta, plantilla inversa con python , pero no es realmente lo que estoy buscando. Simplemente reafirma que esto se puede hacer con re .

EDITAR:

Debo tener en cuenta que $$ es un escape para ‘$’, y no es un token que quiero. $$5 debe hacer a “$ 5”.

Si está bien usar string.format , considere usar la clase string.Formatter que tiene un método parse() :

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

Vea aquí para más detalles.

La clase string.Template tiene el patrón que se usa como un atributo. Puedes imprimir el patrón para obtener los grupos correspondientes.

 >>> print string.Template.pattern.pattern \$(?: (?P\$) | # Escape sequence of two delimiters (?P[_a-z][_a-z0-9]*) | # delimiter and a Python identifier {(?P[_a-z][_a-z0-9]*)} | # delimiter and a braced identifier (?P) # Other ill-formed delimiter exprs ) 

Y para tu ejemplo,

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

Como puede ver arriba, si hace ${one} con tirantes, irá al tercer lugar de la tupla resultante:

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

Entonces, si quieres obtener todas las llaves, tendrás que hacer algo como:

 >>> [s[1] or s[2] for s in string.Template.pattern.findall('${one} is a $lonely $number.$$') if s[1] or s[2]] ['one', 'lonely', 'number'] 

Podrías procesarlo una vez con un diccionario instrumentado que registra las llamadas, o un punto por defecto, y luego verifica lo que pidió.

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

intente str.strip() junto con 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'] 

Tu podrías intentar:

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

¿Por qué quieres evitar las expresiones regulares? Funcionan bastante bien para esto:

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

Para las plantillas, echa un vistazo a re.sub , se puede llamar con callback para hacer casi lo que quieres.

 >>> 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']