Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the best-maintained generic functions implementation for Python?

A generic function is dispatched based on the type of all its arguments. The programmer defines several implementations of a function. The correct one is chosen at call time based on the types of its arguments. This is useful for object adaptation among other things. Python has a few generic functions including len().

These packages tend to allow code that looks like this:

@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

A less dumb example I've been thinking about lately would be a web component that requires a User. Instead of requiring a particular web framework, the integrator would just need to write something like:

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 has a few generic functions implementations. Why would I chose one over the others? How is that implementation being used in applications?

I'm familiar with Zope's zope.component.queryAdapter(object, ISomething). The programmer registers a callable adapter that takes a particular class of object as its argument and returns something compatible with the interface. It's a useful way to allow plugins. Unlike monkey patching, it works even if an object needs to adapt to multiple interfaces with the same method names.

like image 784
joeforker Avatar asked Sep 18 '09 14:09

joeforker


People also ask

What are generic functions in Python?

A generic function is composed of multiple functions implementing the same operation for different types. Which implementation should be used during a call is determined by the dispatch algorithm. When the implementation is chosen based on the type of a single argument, this is known as single dispatch.

What are generic types Python?

Generic Types Generics are not just used for function and method parameters. They can also be used to define classes that can contain, or work with, multiple types. These “generic types” allow us to state what type, or types, we want to work with for each instance when we instantiate the class.

Does Python support generic functions?

Generic is a programming library for Python that provides tools for generic programming. By now, there is only one feature – multiple dispatch.

What is the purpose of a generic function?

Generic functions are functions declared with one or more generic type parameters. They may be methods in a class or struct , or standalone functions. A single generic declaration implicitly declares a family of functions that differ only in the substitution of a different actual type for the generic type parameter.


1 Answers

I'd recommend the PEAK-Rules library by P. Eby. By the same author (deprecated though) is the RuleDispatch package (the predecessor of PEAK-Rules). The latter being no longer maintained IIRC.

PEAK-Rules has a lot of nice features, one being, that it is (well, not easily, but) extensible. Besides "classic" dispatch on types ony, it features dispatch on arbitrary expressions as "guardians".

The len() function is not a true generic function (at least in the sense of the packages mentioned above, and also in the sense, this term is used in languages like Common Lisp, Dylan or Cecil), as it is simply a convenient syntax for a call to specially named (but otherwise regular) method:

len(s) == s.__len__()

Also note, that this is single-dispatch only, that is, the actual receiver (s in the code above) determines the method implementation called. And even a hypothetical

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

is still a single-dispatch function, as only the receiver is used when the method to be called is resolved. The remaining arguments are simply passed on, but they don't affect the method selection.

This is different from multiple-dispatch, where there is no dedicated receiver, and all arguments are used in order to find the actual method implementation to call. This is, what actually makes the whole thing worthwhile. If it were only some odd kind of syntactic sugar, nobody would bother with using it, 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 this example, a call like

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

considers both arguments in order to decide, which method must actually be called.

For a nice usage scenario of generic functions in Python I'd recommend reading the refactored code of the peak.security which gives a very elegant solution to access permission checking using generic functions (using RuleDispatch).

like image 200
Dirk Avatar answered Nov 02 '22 23:11

Dirk