2012-01-14 22 views
9

Devo definire formalmente una funzione prima di poterla usare come elemento di un dizionario?Come dichiarare un dizionario con funzione inline

def my_func(): 
    print 'my_func' 

d = { 
    'function': my_func 
} 

Preferisco definire la funzione in linea. Ho appena provato a digitare cosa voglio fare, ma le politiche di spazio bianco della sintassi python rendono molto difficile definire una funzione inline all'interno di un ditt. C'è un modo per fare questo?

+3

Puoi spiegare perché vorresti farlo? Se vuoi recuperare un valore da una funzione, puoi inserirlo nel dizionario usando 'd ['function'] = my_func()'; se vuoi una funzione che non ti servirà mai più, potresti usare un 'lambda'. – Makoto

+2

Un lambda non funziona per me, perché ho bisogno di istruzioni all'interno della funzione. La funzione non è banale. Voglio evitare di dichiarare la funzione se posso. Voglio la funzione dichiarata in linea. –

+0

Come prevedete di essere in grado di utilizzare tutto ciò che non è definito al momento (non la posizione del file fisico) deve essere usato? Python definisce una funzione eseguendo un'istruzione 'def'. Python definisce il tuo dict eseguendo il tuo 'd = {etc etc etc}'. –

risposta

13

La risposta sembra essere che non esiste un modo per dichiarare una funzione inline una definizione del dizionario in Python. Grazie a tutti coloro che hanno avuto il tempo di contribuire.

+2

Questa è la risposta più corretta. Mentre è bello sapere circa cinque modi per fare qualcosa di simile in Python, non c'è modo di fare ciò che è così semplice in Javascript con funzioni anonime diverse da lambda, che sono limitate a semplici funzioni che possono stare su una riga . –

+0

Vorrei indicare il motivo per cui Python non fa ciò che JavaScript fa per la leggibilità e una conseguenza della scelta di utilizzare lo spazio bianco per indicare blocchi di controllo piuttosto che parentesi. È stato solo dopo aver usato Python per anni e sono stato in grado di eliminare facilmente le "buone parti" di JavaScript. –

+0

Concordato che JavaScript ha ottenuto questa parte correttamente, è così facile e leggibile dichiarare alcune funzioni in linea con l'oggetto che le contiene –

3

Alcune funzioni possono essere facilmente 'inline' anonimo con le espressioni lambda, ad es .:

>>> d={'function': lambda x : x**2} 
>>> d['function'](5) 
25 

Ma per qualsiasi cosa semi-complesso (o utilizzando le istruzioni) si dovrebbe probabilmente solo definirli in anticipo.

+2

Grazie, ma un lambda è insufficiente. Ho bisogno di dichiarazioni all'interno della funzione. –

4

considerano utilizzando lambda, ma si noti che lambda deve esserefunzioni e non possono contenere istruzioni (vedi http://docs.python.org/reference/expressions.html#lambda)

esempio

d = { 'func': lambda x: x + 1 } 
# call d['func'](2) will return 3 

Inoltre, si noti che in python2, print non è una funzione. In modo da avere a che fare né:

from __future__ import print_function 

d = { 
    'function': print 
} 

o utilizzare sys.stdout.write invece

d = { 
    'function': sys.stdout.write 
} 
+0

Oggetto contro il fraseggio "deve essere funzioni, non procedure". Indipendentemente da quale delle definizioni conflittuali di quei termini che usi, nessuno si adatta davvero. L'unica restrizione è che il corpo del lambda deve essere una singola espressione. – delnan

+0

@delnan Grazie per il chiarimento. Risposta modificata. Ero solito prendere i flussi di controllo (ad esempio 'if'' for') come procedure, quindi portare all'ambiguità. – qiao

+0

Grazie per la nota su 'stampa'. –

6

Avete veramente bisogno di un dizionario, o semplicemente getitem accesso?

In quest'ultimo caso, quindi utilizzare una classe:

>>> class Dispatch(object): 
...  def funcA(self, *args): 
...   print('funcA%r' % (args,)) 
...  def funcB(self, *args): 
...   print('funcB%r' % (args,)) 
...  def __getitem__(self, name): 
...   return getattr(self, name) 
... 
>>> d = Dispatch() 
>>> 
>>> d['funcA'](1, 2, 3) 
funcA(1, 2, 3) 
+0

Questo è un buon modo se in realtà vuole istanziare gli oggetti, si spera perché contengono qualche stato utile.Il punto chiave qui è, che il tuo esempio non mostra, è che la classe dovrebbe contenere qualche stato utile che meriti di essere istanziata, altrimenti può semplicemente usare il metodo descritto nella mia risposta per accedere alle funzioni tramite una chiave di stringa. –

+0

@DerekLitz. La praticità batte la purezza. La domanda dell'OP riguarda le funzioni di inlining all'interno di un dizionario, che è molto simile a ciò che è già una classe python (cioè un wrapper attorno a un dict). Il mio esempio fornisce la funzionalità di cui l'OP ha realmente bisogno in una forma leggibile e facilmente manutenibile. – ekhumoro

+0

Penso che tu abbia perso il mio punto. Istanziare l'oggetto d sopra non ha uno scopo significativo, che può portare a confusione. Aggiunge complessità extra dove nessuno è necessario (dal momento che non hai uno stato significativo). D sembra comportarsi come un imputato a prima vista, ma in realtà no. Non si sta utilizzando una classe in cui si utilizza un oggetto Dispatch per fornire la funzionalità e IMHO come scritto l'oggetto Dispatch non aggiunge funzionalità significative né semplificazioni. __getitem__ dovrebbe essere usato per semplificare l'uso di una classe complessa, riutilizzando i comuni idiomi Python, come fa sqlalchemy per le query. –

3

Non v'è alcuna buona ragione per voler scrivere questo utilizzando un dizionario in Python. È strano e non è un modo comune per le funzioni dello spazio dei nomi.

Il filosofie Python applicabili qui:

Dovrebbe esserci tra-- e preferibilmente solo modo --obvious per farlo.

combinato con

Leggibilità conta.

Facendolo in questo modo rende anche le cose difficili da capire e leggere per il tipico utente Python.

Le cose buone del dizionario in questo caso sono le stringhe di mappa alle funzioni e lo spazio dei nomi all'interno di un dizionario, ma questa funzionalità è già fornita da entrambi i moduli e classi ed è molto più facile da capire da chi ha familiarità con Python.

Esempi: metodo

Modulo:

#cool.py 

def cool(): 
    print 'cool' 

Ora usare il modulo come si sarebbe utilizzando il tuo dict: metodo

import cool 
#cool.__dict__['cool']() 
#update - to the more correct idiom vars 
vars(cool)['cool']() 

Classe:

class Cool(): 
    def cool(): 
     print 'cool' 

#Cool.__dict__['cool']() 
#update - to the more correct idiom vars 
vars(Cool)['cool']() 

Modifica dopo commento sotto:

argparse sembra una buona soluzione per questo problema, quindi non è necessario reinventare la ruota. Se decidi di implementarlo completamente da solo, anche se la fonte argparse dovrebbe darti una buona direzione. Ad ogni modo le sezioni seguenti sembrano applicabili a questo caso d'uso:

15.4.4.5. Oltre sys.argv

A volte può essere utile avere un argomento di analisi ArgumentParser diverso da quelli di sys.argv. Questo può essere ottenuto passando un elenco di stringhe in parse_args(). Ciò è utile per il test al prompt interattivo :

15.4.5.1. Sub-commands¶

ArgumentParser.add_subparsers()

Molti programmi divisi loro funzionalità in una serie di comandi secondari, per esempio, il programma svn può invocare comandi secondari come svn checkout, svn update, e svn commit.

15.4.4.6. L'oggetto Namespace

Potrebbe anche essere utile avere un ArgumentParser per assegnare attributi a un oggetto già esistente, piuttosto che un nuovo oggetto Namespace. Questo può essere raggiunto specificando il namespace = argomento chiave:

Update, ecco un esempio utilizzando argparse

strategizer = argparse.ArgumentParser() 
strat_subs = strategizer.add_subparsers() 
math = strat_subs.add_parser('math') 
math_subs = math.add_subparsers() 
math_max = math_subs.add_parser('max') 
math_sum = math_subs.add_parser('sum') 
math_max.set_defaults(strategy=max) 
math_sum.set_defaults(strategy=sum) 

strategizer.parse_args('math max'.split()) 
Out[46]: Namespace(strategy=<built-in function max>) 

strategizer.parse_args('math sum'.split()) 
Out[47]: Namespace(strategy=<built-in function sum>) 

Vorrei sottolineare le ragioni Suggerirei argparse

  1. Principalmente il requisito di utilizzare stringhe che rappresentano opzioni e opzioni secondarie per mappare le funzioni.
  2. È semplicissimo (dopo aver superato il modulo argparse pieno di funzioni).
  3. Utilizza un modulo di libreria standard Python. Questo fa sì che altri utenti familiarizzino con Python, senza sapere quali sono i tuoi dettagli di implementazione, ed è molto ben documentato per coloro che non lo sono.
  4. Molte funzionalità extra potrebbero essere sfruttate fuori dalla scatola (non la migliore ragione!).

Utilizzando argparse e strategia del modello insieme

Per l'attuazione puro e semplice del modello di strategia, questo è già stato risposto molto bene.

How to write Strategy Pattern in Python differently than example in Wikipedia?

#continuing from the above example 
class MathStudent(): 
    def do_math(self, numbers): 
     return self.strategy(numbers) 


maximus = strategizer.parse_args('math max'.split(), 
            namespace=MathStudent()) 

sumera = strategizer.parse_args('math sum'.split(), 
           namespace=MathStudent()) 

maximus.do_math([1, 2, 3]) 
Out[71]: 3 

sumera.do_math([1, 2, 3]) 
Out[72]: 6 
+0

Quello che sto davvero cercando di implementare qui è il modello di strategia. Voglio un dizionario che contenga diversi livelli di opzioni e funzioni, quindi posso fare qualcosa del genere: 'd [opzione1] [opzione2] (args)'. Ma voglio farlo nel modo più semplice possibile. In Groovy o JavaScript, puoi chiudere in linea le mappe. Potrebbe essere che ho bisogno di creare una classe per le funzioni che desidero essere opzioni all'interno della strategia. –

+0

Ci sono molte buone ragioni per voler fare ciò che l'OP sta facendo. È una tecnica molto comune conosciuta come [Dynamic Dispatch] (http://en.wikipedia.org/wiki/Dynamic_dispatch). – ekhumoro

+0

@ekhumoro Il linguaggio Python implementa la distribuzione dinamica ... l'OP non implementa la spedizione dinamica ... –

2

Il punto delle funzioni di inlining consiste nell'offuscare la distinzione tra dizionari e istanze di classe. In javascript, ad esempio, questa tecnica rende molto piacevole scrivere classi di controllo che hanno poca riusabilità. Inoltre, e molto utile, l'API è quindi conforme ai noti protocolli di dizionario, essendo esplicativo (gioco di parole).

Si può fare questo in python - semplicemente non sembra un dizionario! In effetti, puoi usare la parola chiave class in QUALSIASI scope (cioè una classe def in una funzione, o una classe def dentro una classe def), ed i suoi figli possono essere il dictonary che stai cercando; basta esaminare gli attributi di una definizione come se fosse un dizionario javascript.

Esempio come se fosse reale:

somedict = { 
    "foo":5, 
    "one_function":your method here, 
    "two_function":your method here, 
} 

è in realtà realizzato come

class somedict: 
    foo = 5 
    @classmethod 
    def one_method(self): 
     print self.foo 
     self.foo *= 2; 

    @classmethod 
    def two_method(self): 
     print self.foo 

Così che si può poi dire:

somedict.foo   #(prints 5) 
somedict.one_method() #(prints 5) 
somedict.two_method() #(prints 10) 

E in questo modo, si ottiene gli stessi raggruppamenti logici che faresti con il tuo "inlining".

Problemi correlati