2009-09-18 9 views
8

Numer generic function jest wywoływany na podstawie typu wszystkich jego argumentów. Programista określa kilka implementacji funkcji. Prawidłowy jest wybierany w czasie połączenia w oparciu o typy jego argumentów. Przydaje się to między innymi do adaptacji obiektów. Python ma kilka ogólnych funkcji, w tym len().Jakie są najlepiej utrzymane implementacje funkcji ogólnych dla Pythona?

Pakiety te mają tendencję, aby umożliwić kod, który wygląda tak:

@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 

Mniej głupi przykład myślałam o ostatnio byłby składnikiem internetowa, która wymaga od użytkownika. Zamiast wymagające szczególnej ramy internetową, integrator po prostu trzeba napisać coś takiego:

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 ma kilka rodzajowe funkcje implementacje. Dlaczego wybrałbym jedną nad innymi? W jaki sposób ta implementacja jest używana w aplikacjach?

Jestem zaznajomiony z Zope's zope.component.queryAdapter(object, ISomething). Programista rejestruje adapter do wywołania, który przyjmuje jako argument określoną klasę obiektu i zwraca coś kompatybilnego z interfejsem. Jest to przydatny sposób na włączenie wtyczek. W przeciwieństwie do łatania małp, działa nawet wtedy, gdy obiekt musi się dostosować do wielu interfejsów o tych samych nazwach metod.

+1

Funkcja 'len' po prostu wywołuje metodę' __len__' obiektu. Czy to masz na myśli funkcję "rodzajową"? Jeśli tak, istnieje stosunkowo niewiele funkcji, które zachowują się w ten sposób. Czy masz coś innego na myśli? Jeśli tak, wybierz lepszy przykład. –

+0

Czy mówisz o polimorfizmie? A może mówisz o poprawianiu małp istniejących typów, aby dodać polimorfizm? –

+0

wydaje się, że (w oparciu o link? I wikipedia) joeforker chce zaimplementować przeciążone funkcje. Wierzę, że odnosi się do "generycznego" w tym sensie, że funkcja jest wywoływana przez 1 nazwę, ale wykonuje inny kod na podstawie przekazanych parametrów. Zatem: My_function (a) -> execute function_a My_function (b) -> execute function_b Jest to bardziej stylowe realizacja analizowania ** kwargs dostać których kod do wykonania – Mark

Odpowiedz

7

Polecam bibliotekę PEAK-Rules P. Eby. Przez tego samego autora (przestarzały) jest pakiet RuleDispatch (poprzednik reguł PEAK). Ten ostatni nie jest już utrzymywany IIRC.

PEAK-Rules ma wiele fajnych cech, z których jedna jest (no, nie łatwo, ale) rozszerzalna. Poza "klasyczną" wysyłką na typy ony, zawiera wysyłkę na arbitralne wyrażenia jako "strażnicy".

Funkcja len() nie jest prawdziwa funkcja rodzajowe (przynajmniej w sensie pakietów wymienionych powyżej, a także w tym sensie, termin ten jest stosowany w językach jak Common Lisp, Dylan lub Cecil), gdyż jest to po prostu wygodna składnia wywołania specjalnie nazwie (ale w inny sposób regularny) metoda:

len(s) == s.__len__() 

Należy również pamiętać, że jest to jedno-wysyłka tylko, że jest rzeczywista odbiornik (s w powyższym kodzie) określa implementację metody nazywa. A nawet hipotetyczne

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

jest jeszcze funkcja pojedynczej wysyłki, jak tylko odbiornik jest stosowana, jeśli metoda być nazywany jest rozwiązany. Pozostałe argumenty są po prostu przekazywane, ale nie mają wpływu na wybór metody.

To różni się od wielokrotnego wysyłania, gdzie nie ma dedykowanego odbiornika, a wszystkie argumenty są używane w celu znalezienia rzeczywistej implementacji metody do wywołania. To właśnie sprawia, że ​​wszystko jest warte zachodu. Gdyby to był jakiś dziwny rodzaj cukru syntaktycznego, nikt by nie zawracał sobie głowy jego używaniem, 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() 

W tym przykładzie wywołanie jak

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

uważa zarówno argumentów w celu podjęcia decyzji, która metoda musi być rzeczywiście nazwie.

Dla dobrego zastosowania ogólnych funkcji w Pythonie, polecam lekturę refaktoryzowanego kodu peak.security, który daje bardzo eleganckie rozwiązanie dostępu do sprawdzania uprawnień przy użyciu ogólnych funkcji (przy użyciu RuleDispatch).

+0

co http: // PyPI .python.org/pypi/simplegeneric również przez PJE? – joeforker

+0

AFAIK jest to dość prosta implementacja, która obsługuje tylko wysyłkę opartą na typie. Tak naprawdę nie zajrzałem do tego, więc nie mogę nic o tym powiedzieć. Moje doświadczenia są głównie z 'RuleDispatch' i jego następcą. – Dirk

+1

Simplegeneric jest DUŻO prostszy, ale, jak sądzę, nie tak aktywnie konserwowany jak bardziej złożony i bogatszy PEAK. –

0

można wykorzystywać konstrukcję tak:

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

w tym przypadku args będzie wykaz wszystkich nienazwanych argumentów i kwargs będzie słownikiem nazwanych nich. Stąd możesz wykryć ich typy i postępować odpowiednio.

+1

To nie ma sensu w kontekście zmienionego pytania. –

+0

Ya, wydawało się to właściwą odpowiedzią w tym czasie. – defrex

0

Nie widzę sensu w tych "ogólnych" funkcjach. Brzmi jak prosty polimorfizm.

Twoje "ogólne" cechy mogą być realizowane w ten sposób bez uciekania się do jakiejkolwiek identyfikacji typu run-time.

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

Twoje podejście nie pozwala na wywoływanie 'dumbexample (1)' zgodnie ze specyfikacją OP (i jak pozwalają na to wszystkie standardowe paczki funkcji) - potrzebujesz uciążliwego owijania przy każdym takim połączeniu, zwykle z przełączaniem w oparciu o typy (eep). Ale polimorfizm oparty na JEDNYM typie jest nadal z grubsza OK: gdzie klasyczny OOP całkowicie się rozpada, a ogólne funkcje (jak w Common Lisp, Dylan, ...) naprawdę świecą się w rozsyłaniu opartym na typach MULTIPLE argumentów (unikając klutów takich jak Visitor, __radd__ vs __add__, a zillion innych kludges OOP potrzebuje tam). –

+0

@Alex Martelli: I, prawdopodobnie, unikając problemów związanych z handlem i podwójną wysyłką. Pytanie OP - i przykład - nie dotykało tego. Wciąż jestem niejasny w * rzeczywistym * pytaniu. A może pytanie było tylko hipotetyczne. –

Powiązane problemy