[Python-Dev] Class Methods
Thomas Heller
thomas.heller at ion-tof.com
Thu Apr 26 08:13:23 EDT 2001
[Me]
> > ...I would like to define in python in this way:
> >
> > class POINT(Structure):
> > _names = "x", "y"
> > _format = "ii" # specified in the way the struct-module uses
> >
> > and use in this way:
> >
> > p = POINT(10, 20)
> > p.x # would return 10
> > p.x = 100
> >
> > POINT.size_in_bytes
> > or
> > POINT.size_in_bytes() # would return the same as the C code
> > sizeof(struct POINT)
> >
> > I would like to retrieve the size_in_bytes attribute _before_
> > the first instance of POINT is created, so I cannot calculate
> > this in the first __init__() call, a magic class method,
> > automatically called just after the class creation, would come
> > very handy.
[Tim Peters]
> That's reasonable enough -- but you know there are many ways to accomplish
> this already, right? I'm afraid I greatly *prefer* an explicit call to some
> sort of do_magic_stuff_to_class(POINT) after the class definition, to make it
> clear that you're playing unexpected games with the class definition (e.g.,
> there is nothing in the body of POINT to suggest to anyone other than the
> original author-- and even then only right after writing it --that instances
> will grow .x and .y attributes out of thin air; and I don't care if you have
> to write an extra line of code if in return readers get a *clue* that dynamic
> trickery may be afoot).
???: The magic is hidden in the superclass(superobject), and every
python programmer knowns this. Of course, usually he only expects
magic behaviour in the instances, not in the class itself.
On the other hand, Structure can be a metaclass, or implemented in C,
anything - so it may not be easy to read the source.
Third point: Forgetting to call your do_magic_stuff_to_class(POINT)
may lead to hard to diagnose errors much later, it would be better
to have a traceback like this:
File "magic.py", line 5:
class POINT(Structure): pass
AttributeError: Structure subclasses must define _names and _format attributes
An alternative, of course, would be to use a factory function
for classes:
POINT = create_structure("POINT", names=("x", "y"), format="ii")
but I would loose the (easy) ability to override or implement instance
methods. Also: Does anyone expect the spani^H^H^H^H^H functions to create
and return classes ?
>
> In my experience, there are three kinds of metaclass users in Python: those
> who blissfully ignore the possibilities; those who endured the metaclass
> essay to the end, tried it once or twice, shrugged their shoulders and moved
> on; and those who lived to regret taking it seriously. It makes code a
> godawful obscure mess, and so even the relatively few people who understand
> it steer clear of it whenever possible (for example, the Python-level hook
> has been in since 1.5a3, and it *still* hasn't found a use in the std
> library).
There are two meta-class hooks in python: The Don Beaudry hook,
and the Guido hook (as it was called by Just, who invented a third
kind of hook).
Don's hook has at least two uses hat I know of: Jim Fulton's ExtensionClass, and
Don's objectmodule. (The boost python lib may be the third use, apologies
if I missed others.)
These extensions are really used in production code: they actually
implement a different objectmodel, with useful behaviour. To see
what they do, you normally refer to their docs, not to the source code.
You simply use them - that's all.
I've never seen (apart from demo/metaclasses) any use of Guido's
hook.
You can implement metaclasses in python for experiments - until
your head explodes ;-). I found another reason not to use them:
They are very slow - because you do a lot of attribute lookup
in pure python iterating over the bases tree.
> I understand that you're not after something as encompassing as the metaclass
> hook, but if what you want *can* be spelled with an explicit call, I'm
> inclined to let it rest there.
I don't know.
I have the feeling that class-methods would be a useful addition
to the python toolbox. My requirements spelled out above could
easily be satisfied with a magic __class_init__ class-method,
but that does not seem the way to go, __class_getattr__ and so
on would follow shortly...
The meta-hook(s) are powerful enough to implement all this,
Don's objectmodule may provide the best implementation so far.
Maybe we should stick with this.
[Tim implements my Structure class]
> import struct
>
> def do_magic_stuff(c):
> c.size_in_bytes = struct.calcsize(c._format)
>
> class Structure:
> def __init__(self, *args):
> if len(self._names) != len(args):
> raise TypeError("wrong # of args")
> self.__set_attrs_from_values(args)
>
> def __set_attrs_from_values(self, values):
> i = 0
> while i < len(values):
> setattr(self, self._names[i], values[i])
> i += 1
>
> def pack(self):
> values = [getattr(self, attr) for attr in self._names]
> return struct.pack(self._format, *values)
>
> def unpack(self, blob):
> values = struct.unpack(self._format, blob)
> self.__set_attrs_from_values(values)
>
> class POINT(Structure):
> _names = "x", "y"
> _format = "ii"
> do_magic_stuff(POINT)
My own implementation uses a packer object (named schema)
implemented in C, shared among class instances.
Adapting it to your example:
import schema
def do_magic_stuff(c):
c._schema = schema.schema(c._names, c._format)
c._size_in_bytes = c._schema.size_in_bytes()
packing and unpacking is transparent:
class Structure:
def __getattr__(self, name):
return self.__class__._schema.getfield(self._buffer, name)
def __setattr__(self, name, value):
self.__class__._schema.setfield(self._buffer, name, value)
self._buffer is an object representing a memory block,
containing the 'struct POINT' data.
Thomas
More information about the Python-list
mailing list