2009-09-18 13 views
8

A generic function viene inviato in base al tipo di tutti i suoi argomenti. Il programmatore definisce diverse implementazioni di una funzione. Quello corretto viene scelto al momento della chiamata in base al tipo dei suoi argomenti. Questo è utile per l'adattamento dell'oggetto tra le altre cose. Python ha alcune funzioni generiche tra cui len().Qual è l'implementazione delle funzioni generiche meglio gestite per Python?

Questi pacchetti tendono a consentire il codice che assomiglia a questo:

@when(int) 
def dumbexample(a): 
    return a * 2 

@when(list) 
def dumbexample(a): 
    return [("%s" % i) for i in a] 

dumbexample(1) # calls first implementation 
dumbexample([1,2,3]) # calls second implementation 

Un esempio meno stupido Ho pensato ultimamente sarebbe un componente Web che richiede un utente. Invece di richiedere un particolare framework web, l'integratore avrebbe solo bisogno di scrivere qualcosa di simile:

class WebComponentUserAdapter(object): 
    def __init__(self, guest): 
     self.guest = guest 
    def canDoSomething(self): 
     return guest.member_of("something_group") 

@when(my.webframework.User) 
componentNeedsAUser(user): 
    return WebComponentUserAdapter(user) 

Python ha alcune funzioni generiche implementazioni. Perché dovrei sceglierne uno rispetto agli altri? Come viene utilizzata questa implementazione nelle applicazioni?

Ho familiarità con Zope zope.component.queryAdapter(object, ISomething). Il programmatore registra un adattatore richiamabile che accetta una determinata classe di oggetti come argomento e restituisce qualcosa compatibile con l'interfaccia. È un modo utile per consentire i plugin. A differenza delle patch delle scimmie, funziona anche se un oggetto deve adattarsi a più interfacce con gli stessi nomi di metodo.

+1

La funzione 'len' chiama semplicemente il metodo' __len__' di un oggetto. È questo che intendi per funzione "generica"? Se è così, ci sono relativamente poche funzioni che si comportano così. Hai qualcos'altro in mente? Se è così, per favore scegli un esempio migliore. –

+0

Stai parlando di polimorfismo? O stai parlando di tipi esistenti di patch delle scimmie per aggiungere il polimorfismo? –

+0

sembra (basato sul collegamento "e wikipedia fornito") joeforker sta cercando di implementare funzioni sovraccariche. Credo che si riferisca a "generico" nel senso che la funzione è chiamata da 1 nome ma esegue codice diverso in base ai parametri passati. Così: my_function (a) -> execute function_a my_function (b) -> execute function_b E 'un più elegante implementazione di analizzare i ** kwargs per ottenere che il codice da eseguire – Mark

risposta

7

Consiglierei la libreria PEAK-Rules di P. Eby. Dallo stesso autore (tuttavia deprecato) è il pacchetto RuleDispatch (il predecessore di PEAK-Rules). Quest'ultimo non è più mantenuto IIRC.

PEAK-Rules ha molte caratteristiche interessanti, un essere, che è (beh, non facilmente, ma) estendibile. Oltre al dispatch "classico" su tipi, è disponibile l'invio su espressioni arbitrarie come "guardiani".

La funzione len() non è una vera funzione generica (almeno nel senso dei pacchetti menzionati sopra, e anche nel senso, questo termine è usato in lingue come Common Lisp, Dylan o Cecil), in quanto è semplicemente un comodo sintassi per una chiamata a nome appositamente (ma altrimenti normale) metodo:

len(s) == s.__len__() 

noti inoltre, che questo è solo il rinvio solo, cioè, il ricevitore effettivo (s nel codice sopra) determina l'implementazione del metodo chiamato. E anche un ipotetico

def call_special(receiver, *args, **keys): 
    return receiver.__call_special__(*args, **keys) 

è ancora una funzione singola spedizione, come solo il ricevitore viene utilizzato quando il metodo da chiamare è stato risolto. Gli argomenti rimanenti vengono semplicemente passati, ma non influenzano la selezione del metodo.

Questo è diverso dal dispatch multiplo, in cui non è presente un ricevitore dedicato e tutti gli argomenti vengono utilizzati per trovare l'effettiva implementazione del metodo da chiamare. Questo è, ciò che rende veramente utile il tutto. Se fosse solo uno strano tipo di zucchero sintattico, nessuno si preoccuperebbe di usarlo, IMHO.

from peak.rules import abstract, when 

@abstract 
def serialize_object(object, target): 
    pass 

@when(serialize_object, (MyStuff, BinaryStream)) 
def serialize_object(object, target): 
    target.writeUInt32(object.identifier) 
    target.writeString(object.payload) 

@when(serialize_object, (MyStuff, XMLStream)) 
def serialize_object(object, target): 
    target.openElement("my-stuff") 
    target.writeAttribute("id", str(object.identifier)) 
    target.writeText(object.payload) 
    target.closeElement() 

In questo esempio, una chiamata come

serialize_object(MyStuff(10, "hello world"), XMLStream()) 

ritiene entrambi argomenti per decidere quale metodo deve effettivamente essere chiamato.

Per un piacevole scenario di utilizzo di funzioni generiche in Python, consiglierei di leggere il codice refactored dello peak.security che offre una soluzione molto elegante per accedere al controllo delle autorizzazioni utilizzando funzioni generiche (utilizzando RuleDispatch).

+0

Che dire http: // PyPI .python.org/pypi/simplegeneric anche da PJE? – joeforker

+0

AFAIK questa è un'implementazione piuttosto semplice, che fa solo la distribuzione basata su testo. Non ho davvero esaminato il problema, quindi non posso dire nulla al riguardo. Le mie esperienze sono principalmente con 'RuleDispatch' e il suo successore. – Dirk

+1

simplegeneric è FAR più semplice, ma, penso, non così attivamente mantenuto come il PEAK più complesso e ricco. –

0

È possibile utilizzare una struttura come questa:

def my_func(*args, **kwargs): 
    pass 

In questo caso args sarà un elenco di tutti gli argomenti senza nome, e kwargs sarà un dizionario di quelli nominati. Da qui è possibile rilevare i loro tipi e agire come appropriato.

+1

Questo non ha senso nel contesto della domanda rivista. –

+0

Ya, sembrava la risposta giusta al momento. – defrex

0

Non riesco a vedere il punto in queste funzioni "generiche". Sembra un semplice polimorfismo.

Le funzionalità "generiche" possono essere implementate in questo modo senza ricorrere ad alcun tipo di identificazione runtime.

class intWithDumbExample(int): 
    def dumbexample(self): 
     return self*2 

class listWithDumbExmaple(list): 
    def dumpexample(self): 
     return [("%s" % i) for i in self] 

def dumbexample(a): 
    return a.dumbexample() 
+1

L'approccio non consente le chiamate a 'dumbexample (1)' secondo le specifiche dell'OP (e come consentito da tutti i pacchetti di funzioni generiche) - è necessario avvolgere in modo ingombrante ogni chiamata, tipicamente con commutazione basata sui tipi (eep). Ma il polimorfismo basato su UNO tipo è ancora approssimativamente OK: dove l'OOP classico si sbriciola completamente e le funzioni generiche (come in Common Lisp, Dylan, ...) brillano veramente nell'invio basato su tipi di argomenti MULTIPLE (evitando klutzes come Visitor, __radd__ vs __add__, e uno o più zlioni di OOP occorrono lì). –

+0

@Alex Martelli: E, presumibilmente, evitando i problemi coercitivi e problemi di doppia spedizione. La domanda dell'OP - e l'esempio - non hanno toccato nulla di tutto ciò. Non sono ancora chiaro sulla domanda * reale *. O forse la domanda era solo ipotetica. –

Problemi correlati