[Python-3000] Some canonical use-cases for ABCs/Interfaces/Generics (original) (raw)

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