[Python-3000] Some canonical use-cases for ABCs/Interfaces/Generics
Talin
talin at acm.org
Wed May 2 05:10:53 CEST 2007
One of my concerns in the ABC/interface discussion so far is that a lot
of the use cases presented are "toy" examples. This makes perfect sense
considering that you don't want to have to spend several pages
explaining the use case. But at the same time, it means that we might be
solving problems that aren't real, while ignoring problems that are.
What I'd like to do is collect a set of "real-world" use cases and
document them. The idea would be that we could refer to these use cases
during the discussion, using a common terminology and shorthand examples.
I'll present one very broad use case here, and I'd be interested if
people have ideas for other use cases. The goal is to define a small
number of broadly-defined cases that provide a broad coverage of the
problem space.
====
The use case I will describe is what I will call "Object Graph
Transformation". The general pattern is that you have a collection of
objects organized in a graph which you wish to transform. The objects in
the graph may be standard Python built-in types (lists, tuples, dicts,
numbers), or they may be specialized application-specific types.
The Python "pickle" operation is an example of this type of
transformation: Converting a graph of objects into a flat stream format
that can later be reconstituted back into a graph.
Other kinds of transformations would include:
Serialization: pickling, marshaling, conversion to XML or JSON, ORMs
and other persistence frameworks, migration of objects between runtime
environments or languages, etc.
Presentation: Conversion of a graph of objects to a visible form,
such as a web page.
Interactive Editing: The graph is converted to a user editable form,
a la JavaBeans. An example is an user-interface editor application which
allows widgets to be edited via a property sheet. The object graph is
displayed in a live "preview" window, while a "tree view" of object
properties is shown in a side panel. The transformation occurs when the
objects in the graph are transformed into a hierarchy of key/value
properties that are displayed in the tree view window.
These various cases may seem different but they all have a similar
structure in terms of the roles of the participants involved. For a
given transformation, there are 4 roles involved:
1) The author of the objects to be transformed.
2) The author of the generic transform function, such as "serialize".
3) The author of the special transform function for each specific class.
4) The person invoking the transform operation within the application.
We can give names to these various bits of code if we wish, such as the
"Operand", the "General Operator", the "Special Operator", and the
"Invocation". But for now, I'll simply refer to them by number.
Using the terminology of generic functions, (1) is the author of the
argument that is passed to the generic function, (2) is the author of
the original "generic" function, (3) is the author of the overloads of
the generic function, and (4) is the person calling the generic function.
Each of these authors may have participated at different times and may
be unaware of each other's work. The only dependencies is that (3) must
know about (1) and (2), and (4) must know about (2).
Note that if any of these participants *do* have prior knowledge of the
others, then the need for a generic adaption framework is considerably
weakened. So for example, if (2) already knows all of the types of
objects that are going to be operated on, then it can simply hard-code
that knowledge into its own implementation. Similarly, if (1) knows that
it is going to be operated on in this way, then it can simply add a
method to do that operation. Its only when the system needs to be N-way
extensible, where N is the number of participants, that a more general
dispatch solution is required.
A real-world example of this use case is the TurboGears/TurboJSON
conversion of Python objects into JSON format, which currently uses
RuleDispatch to do the heavy lifting.
@jsonify.when(Person)
def jsonify_person(obj):
# Code to convert a Person object to a dict
# of properties which can be serialized as JSON
In this example, the "Person" need never know anything about JSON
formatting, and conversely the JSON serialization framework need know
nothing about Person objects. Instead, this little adaptor function is
the glue that ties them together.
This also means that built-in types can be serialized under the new
system without having to modify them. Otherwise, you would either have
to build into the serializer special-case knowledge of these types, or
you would have to restrict your object graph to using only special
application-specific container and value types. Thus, a list of Person
objects can be a plain list, but can still be serialized using the same
persistence framework as is used for the Person object.
===
OK that is the description of the use case. I'd be interested to know
what uses cases people have that fall *outside* of the above.
-- Talin
More information about the Python-3000
mailing list