Re: Small hack to help interactively identify components of an object

Donald Beaudry (don@vicorp.com)
Fri, 16 Sep 94 16:37:53 EDT

> I tend to rely on the machine rather than my memory for remembering
> things about the language and language environments that i use.
> That's why (or is it the other way around?) i take so well to
> interpreted languages like python - when i need to remember what
> sequence operations are available to me, i do:

>>> [].__methods__
> ['append', 'count', 'index', 'insert', 'remove', 'reverse', 'sort']

> I'd like to see this capability generalized to be more comprehensive.
> So, for example, even the attribute-less objects like strings and
> tuples can be investigated this way. I figure it would not be hard
> (given good knowledge of the system) to make a 'features()' builtin,
> or something, that unfailingly returns a list of the methods
> associated, even implicitly, with an object. Ie, you would see things
> like __getitem__ and __length__ listed for strings and tuples, instead
> of getting "TypeError: attribute-less object" when you try to do a
> '().__methods__'.

I have implemented what I consider to be a fairly comprehensive type
system as an extention to Python. With respect to your comments, a
key feature of this type system is that all types become self
describing. Currently, there is only so much that you can find out by
poking at an object. The list of methods is a good example, but just
try to get the list of attributes from a built-in object. You can't
do it because this information is always hardcoded into the object's
getattr() routine. In fact, since each object is free to implement
getattr() in any way it sees fit, there is no real guarantee that the
__methods__ attribute will even work for any given object. The system
I have developed adds a bit more structure to the definition of an
object, and threfore makes it possible to guarantee self describing
behavior.

Originally, the system was developed to make it easier to interface to
C data structures. The idea was to replace Guido's struct module with
something that was more easily extensible. In my opinion, a major
flaw with the struct module is that you cannot add new types to it
without changing the source to the struct module itself (the
marshaling stuff has the same problem, but I'll worry about that
later). Also, you must unpack a structure into a tuple and then index
the tuple by field number. This is not very intuitive (structures are
better than tuples because they provide symbolic access) and moving
everything into a dictionary is not very efficient.

My current implementation allows for the dynamic creation of new types
(real type objects just like those returned by the type() function)
that can be used to represent C structures, arrays, enums, and
bitsets.

With this system you can declare a structure like

struct('foo', (('a', 'char'), ('b', 'double')))

This creates a new type objects that describes foo objects. Given
that this type now exists, you can create an instance of simply by
saying

x = foo('x', 10.0)

then
type(x) == foo

would evaluate to true. Reading and writing to the structure object
is simple and intuitive.

x.a = '1'
y.b = 100.0

The interesting part, at least with respect to your comments, is that
foo is not really a typeobject, but is actually a sub-class of
typeobject. So as far as the Python interpreter is concerned, it is
just as good as a typeobject. Like any good sub-class, it acts just
like its base class, but does more. In this case, you can apply the
function call operator to instantiate it and you can ask it to give up
its list of attributes

>>> foo.type_desc.attributes
(('a' 'char' (R/W) 0), ('b' 'double' (R/W) 8))

Note that a structure defined this way must perform a conversion to a
Python object each time a field is read and perform a conversion from
a Python object each time a field is written. This can be time
consuming. If there is no desire for structure equivalance with C, it
is more efficient to define the structure fields to hold Python
objects. This can be done by saying

struct('foo', (('a', 'object'), ('b', 'object')))

or more simply by saying

struct('foo', ('a', 'b'))

since 'object' is the default type.

This version of foo has nearly the same memory requirements as that of
a two element tuple, but has the access time of a dictionary. I feel
that the real benefit is that code using structures is more readable
than code using tuples or dictionaries.

In addition to supporting all of the basic C data types, it is very
easy to add new ones. A new type can be added simply by writing a
conversion function that handles reading from and writing to a memory
address. Conversion functions are only necessary for types that are
considered atomic or cannot be described by building on the existing C
types.

I am currently extending the concept to allow Python objects to be
described. The benefits from doing this will be enormous. Most
notably, you will be able to subclass built-in objects from both C and
Python. So from within Python you could actually create new
'built-in' objects (types). This is essentially what the struct
example I gave above does, but structs do not provide for the
implementation of methods. Or said another way, objects are just
structs that have methods. Once this is done, the current set of
built-in objects would need to be modified so that they describe
themselves. This would not be difficult, tedious perhaps, but not
difficult. (What ever happened to that discussion of meta-classes?)

I have given the code for these extentions to Guido and he has started
looking at it. It is going a bit slowly, I suspect, due to the lack
of documentation (I am working on that too). I hope to hear his
reactions to the code sometime soon. If anyone has any ideas,
questions, or comments along these lines, I would like to hear them.
Perhaps if enough interest is generated, it will convince Guido to
move topic a bit higher on his todo list.

-- 
Donald Beaudry                                V.I. Corporation
don@vicorp.com                                Northampton, MA 01060