2010-08-31 11 views
7

Estamos considerando usar Python (IronPython, pero no creo que sea relevante) para proporcionar una especie de soporte "macro" para otra aplicación, que controla un equipo.¿Cuál es una buena manera de proporcionar decoración/metadatos adicionales para los parámetros de la función Python?

Nos gustaría escribir funciones bastante simples en Python, que toman algunos argumentos: estos serían tiempos, temperaturas y posiciones. Las diferentes funciones tomarían diferentes argumentos, y la aplicación principal contendría una interfaz de usuario (algo así como una grilla de propiedad) que permite a los usuarios proporcionar valores para los argumentos de la función de Python.

Por lo tanto, por ejemplo, la función 1 podría tomar un tiempo y una temperatura, y la función 2 podría tomar una posición y un par de veces.

Nos gustaría ser capaces de construir dinámicamente la interfaz de usuario desde el código de Python. Las cosas que son fáciles de hacer son encontrar una lista de funciones en un módulo y (usando inspect.getargspec) para obtener una lista de argumentos para cada función.

Sin embargo, solo una lista de nombres de argumento no es suficiente, idealmente nos gustaría poder incluir más información sobre cada argumento, por ejemplo, es "tipo" (tipo de alto nivel, tiempo, temperatura) , etc., no del tipo de nivel de idioma), y tal vez un "nombre descriptivo" o una descripción.

Entonces, la pregunta es, ¿cuáles son las buenas formas 'pitónicas' de agregar este tipo de información a una función.

Las dos posibilidades se me ha ocurrido son:

  • Usar una convención de nomenclatura estricta para los argumentos, y luego inferir cosas sobre ellos por sus nombres (recuperan utilizando getargspec)

  • inventar nuestra propia meta-lenguaje de docstring (podría ser poco más que CSV) y usar la docstring para nuestros metadatos.

Debido Python parece bastante popular para la construcción de secuencias de comandos en grandes aplicaciones, Me imagino que esto es un problema resuelto con algunas convenciones comunes, pero no han sido capaces de encontrarlos.

+2

Si estaba utilizando python3 , puede usar las anotaciones de funciones: http://www.python.org/dev/peps/pep-3107/ – unutbu

+2

Py.test usa el primer enfoque que menciona para especificar datos para cada una de las pruebas. Funciona bastante bien en su caso. –

Respuesta

7

decoradores son una buena manera de añadir metadatos a las funciones. Añadir una que toma una lista de tipos para añadir a una propiedad o algo .params:

def takes(*args): 
    def _takes(fcn): 
     fcn.params = args 
     return fcn 
    return _takes 

@takes("time", "temp", "time") 
def do_stuff(start_time, average_temp, stop_time): 
    pass 
+4

Considere usar objetos en lugar de cadenas para describir tipos: '@takes (Time, Temp, Time)'. Las cadenas no son muy buenas porque no se puede extender ese modelo sin modificar el núcleo. También su núcleo puede ser sin estado (no es necesario que registre nombres de tipos). Tus objetos deberían saber cómo renderizarse en HTML y cómo validarse. También tiene la ventaja de que puede parametrizar sus tipos: '@takes (Int (max = 100, min = 0))'. –

+0

Sí, eso. Las cuerdas fueron realmente solo demostrativas. Cuando hice esto en el pasado, utilicé funciones que aceptaban cadenas (mi única fuente de entrada) y objetos devueltos que mi programa podía usar (algunas veces después de realizar búsquedas en la base de datos). – nmichaels

4

volvería a usar algún tipo de decorador:

class TypeProtector(object): 
    def __init__(self, fun, types): 
     self.fun, self.types = fun, types 
    def __call__(self, *args, **kwargs) 
     # validate args with self.types 
     pass 
     # run function 
     return fun(*args, **kwargs) 

def types(*args): 
    def decorator(fun): 
     # validate args count with fun parameters count 
     pass 
     # return covered function 
     return TypeProtector(fun, args) 
    return decorator 

@types(Time, Temperature) 
def myfunction(foo, bar): 
    pass 

myfunction('21:21', '32C') 
print myfunction.types 
+0

Gracias por esto, desearía poder darle toda la "respuesta". Aparte de la pregunta original, ¿cuál es el papel de TypeProtector en esto? –

+2

es decir. puede verificar fácilmente si la función está decorada o no: isinstance (TypeProtector, o). Básicamente, modificar objeto de función directamente como en 'fcn.params = args' no es tan buena idea si quieres un código limpio autoexplicable. –

1

La forma 'Pythonic' para hacer esto son function annotations.

def DoSomething(critical_temp: "temperature", time: "time") 
    pass 
+1

¿Qué hay de 2.6? –

+0

para 2.6, él podría usar su – leoluk

+0

Esto fue muy interesante ya que me llevó a varias fuentes útiles de información. Desafortunadamente no puedo usarlo en este proyecto en particular debido a la versión, pero eventualmente, estoy Por supuesto –

0

Para Python 2.x, me gusta usar la cadena de documentación

def my_func(txt): 
    """{ 
    "name": "Justin", 
    "age" :15 
    }""" 
    pass 

y se puede asignar automáticamente al objeto función con este fragmento

for f in globals(): 
    if not hasattr(globals()[f], '__call__'): 
     continue 
    try: 
     meta = json.loads(globals()[f].__doc__) 
    except: 
     continue 
    for k, v in meta.items(): 
     setattr(globals()[f], k, v) 
Cuestiones relacionadas