2010-08-31 16 views
7

Stiamo prendendo in considerazione l'utilizzo di Python (IronPython, ma non credo sia pertinente) per fornire una sorta di supporto "macro" per un'altra applicazione, che controlla un pezzo di equipaggiamento.Qual è un buon modo per fornire ulteriori decorazioni/metadati per i parametri della funzione Python?

Vorremmo scrivere funzioni abbastanza semplici in Python, che richiedono alcuni argomenti: si tratta di cose come orari, temperature e posizioni. Diverse funzioni prenderebbero argomenti diversi e l'applicazione principale conterrebbe un'interfaccia utente (qualcosa come una griglia di proprietà) che consente agli utenti di fornire valori per gli argomenti della funzione Python.

Quindi, ad esempio, la funzione 1 potrebbe richiedere un tempo e una temperatura e la funzione2 potrebbe assumere una posizione e un paio di volte.

Ci piacerebbe essere in grado di creare dinamicamente l'interfaccia utente dal codice Python. Le cose facili da fare sono trovare un elenco di funzioni in un modulo e (usando inspect.getargspec) per ottenere un elenco di argomenti per ciascuna funzione.

Tuttavia, solo una lista di nomi di argomenti non è veramente sufficiente - idealmente vorremmo essere in grado di includere ulteriori informazioni su ciascun argomento - ad esempio, è "tipo" (tipo di alto livello - tempo, temperatura , ecc., non il tipo a livello di lingua), e forse un "nome descrittivo" o una descrizione.

Quindi, la domanda è, quali sono i buoni modi "pitonici" di aggiungere questo tipo di informazioni a una funzione.

Le due possibilità che ho pensato sono:

  • Utilizzare una rigida convenzione di denominazione per gli argomenti, e quindi dedurre roba su di loro dai loro nomi (recuperati utilizzando getargspec)

  • Invent nostra docstring meta-language (potrebbe essere poco più di CSV) e usare la docstring per i nostri metadati.

Perché Python sembra piuttosto popolare per la costruzione di scripting in grandi applicazioni, immagino che questo è un problema risolto con alcune convenzioni comuni, ma non sono stato in grado di trovarli.

+2

Se si sta utilizzando python3 , è possibile utilizzare le annotazioni delle funzioni: http://www.python.org/dev/peps/pep-3107/ – unutbu

+2

py.test utilizza il primo approccio menzionato per specificare i dati per ciascuno dei test. Funziona abbastanza bene nel loro caso. –

risposta

7

decoratori sono un buon modo per aggiungere metadati alle funzioni. Aggiungere uno che prende un elenco dei tipi di aggiungere a una proprietà .params o qualcosa del genere:

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

Considera di utilizzare gli oggetti anziché le stringhe per descrivere i tipi: '@takes (Time, Temp, Time)'. Le stringhe non sono un approccio molto buono perché non è possibile estendere tale modello senza modificare il core. Anche il core può essere senza stato (non è necessario registrare i nomi dei tipi). I tuoi oggetti dovrebbero sapere come eseguire il rendering in HTML e come convalidare se stesso. Inoltre hai il vantaggio di poter parametrizzare i tuoi tipi: '@takes (Int (max = 100, min = 0))'. –

+0

Sì, quello. Le stringhe erano davvero solo dimostrative. Quando ho fatto questo in passato, ho usato le funzioni che accettavano le stringhe (la mia unica fonte di input) e gli oggetti restituiti che il mio programma poteva usare (a volte dopo aver fatto ricerche nel database). – nmichaels

4

vorrei utilizzare un qualche tipo di decoratore:

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

Grazie per questo - vorrei poterti dare tutta la 'risposta'. A parte la domanda originale, qual è il ruolo di TypeProtector in questo? –

+2

ie. puoi facilmente controllare se la funzione è decorata o meno: isinstance (TypeProtector, o). Fondamentalmente la modifica dell'oggetto funzione direttamente come in 'fcn.params = args' non è una buona idea se si desidera un codice auto-spiegabile pulito. –

1

Il modo 'divinatorio' per fare questo sono function annotations.

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

Che dire di 2.6? –

+0

per 2.6, potrebbe usare quello – leoluk

+0

Questo è stato molto interessante in quanto mi porta in diverse utili fonti di informazioni - sfortunatamente non posso usarlo su questo particolare progetto a causa del problema di versione, ma alla fine, lo farò sicuro –

0

per Python 2.x, mi piace usare la docstring

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

e può essere automaticamente assegnare all'oggetto funzione con questo frammento

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) 
Problemi correlati