Abusing Python classes as namespaces
Suppose, not entirely hypothetically, that you are dealing with binary structures and so have a bunch of functions to encode Python values to various different sorts of binary fields and decode those fields back to Python values. These functions clearly come in pairs.
There are various ways of organizing these functions; for
example, you can just give them names like encode_uint16
and
decode_uint16
. One attractive way would be to group the pairs of
functions together in namespaces, for example as uint16.encode
and
uint16.decode
; this becomes even more useful if you add more per
field type functions (for example, a validator function).
Unfortunately, Python does not have general namespace support. There is no convenient way to open up a new namespace and start defining things in it; all of the ways of creating namespaces come with various sorts of baggage. The closest Python comes to pure namespaces without side effects is modules, but you have to put them in separate files and I think that this gets ugly and hard to follow because the modules (and thus the files) are just too small.
(In theory one answer is to add some methods to your value
types. However, even if you have modifiable classes for your value
types, it may be inappropriate to add .encode
and .decode
methods to them because the same value type may have multiple different
possible encodings. And if you're using basic Python values (ints,
strings, etc), you don't even have modifiable classes to add methods
to.)
The only real way to conveniently define multiple namespaces in a single
file is to make them classes. But not conventional classes. Classes
used purely as namespaces will never be instantiated; with no instances
and no per-instance state, none of the 'methods' will actually ever use
self
and so there is no point in having them called with it. In short
we want a class that has only staticmethod
functions, because that
makes them into regular plain old functions:
class uint16(object): @staticmethod def encode(val): return ... @staticmethod def decode(data): return ...
(This class deliberately breaks the usual Python naming convention for classes, because it's nothing like a normal class.)
In some cases it may be useful to have per-class data and some sort of class hierarchy in order to reuse code. That's the time for @classmethod.
PS: having tried this approach out in some of my code, I confess that I'm not sure I like it. The problem for me is an aesthetic one; I think I got the 'namespace' names wrong (I made them too class-like) and I'm not sure I like the @staticmethod's that are sprayed all over the place.
|
|