clanek: Improve style (second paragraph)
[gostyle.git] / sgflib / typelib.py
blob311b24f0d23028d98577391aca5135f1dbe978b1
1 #!/usr/local/bin/python
3 # typelib.py (Type Class Library)
4 # Copyright (c) 2000 David John Goodger
5 #
6 # This software is provided "as-is", without any express or implied warranty.
7 # In no event will the authors be held liable for any damages arising from the
8 # use of this software.
9 #
10 # Permission is granted to anyone to use this software for any purpose,
11 # including commercial applications, and to alter it and redistribute it
12 # freely, subject to the following restrictions:
14 # 1. The origin of this software must not be misrepresented; you must not
15 # claim that you wrote the original software. If you use this software in a
16 # product, an acknowledgment in the product documentation would be appreciated
17 # but is not required.
19 # 2. Altered source versions must be plainly marked as such, and must not be
20 # misrepresented as being the original software.
22 # 3. This notice may not be removed or altered from any source distribution.
24 """
25 ================================
26 Type Class Library: typelib.py
27 ================================
28 version 1.0 (2000-03-27)
30 Homepage: [[http://gotools.sourceforge.net/]] (see sgflib.py)
32 Copyright (C) 2000 David John Goodger ([[mailto:dgoodger@bigfoot.com]]).
33 typelib.py comes with ABSOLUTELY NO WARRANTY. This is free software, and you are
34 welcome to redistribute it and/or modify it under certain conditions; see the
35 source code for details.
37 Description
38 ===========
39 This library implements abstract superclasses to emulate Python's built-in data
40 types. This is useful when you want a class which acts like a built-in type, but
41 with added/modified behaviour (methods) and/or data (attributes).
43 Implemented types are: 'String', 'Tuple', 'List', 'Dictionary', 'Integer',
44 'Long', 'Float', 'Complex' (along with their abstract superclasses).
46 All methods, including special overloading methods, are implemented for each
47 type-emulation class. Instance data is stored internally in the 'data' attribute
48 (i.e., 'self.data'). The type the class is emulating is stored in the class
49 attribute 'self.TYPE' (as given by the built-in 'type(class)'). The
50 'SuperClass.__init__()' method uses two class-specific methods to instantiate
51 objects: '_reset()' and '_convert()'.
53 See "sgflib.py" (at module's homepage, see above) for examples of use. The Node
54 class is of particular interest: a modified 'Dictionary' which is ordered and
55 allows for offset-indexed retrieval."""
58 # Revision History
60 # 1.0 (2000-03-27): First public release.
61 # - Implemented Integer, Long, Float, and Complex.
62 # - Cleaned up a few loose ends.
63 # - Completed docstring documentatation.
65 # 0.1 (2000-01-27):
66 # - Implemented String, Tuple, List, and Dictionary emulation.
68 # To do:
69 # - Implement Function? File? (Have to come up with a good reason first ;-)
72 class SuperType:
73 """ Superclass of all type classes. Implements methods common to all types.
74 Concrete (as opposed to abstract) subclasses must define a class
75 attribute 'self.TYPE' ('=type(Class)'), and methods '_reset(self)' and
76 '_convert(self, data)'."""
78 def __init__(self, data=None):
79 """
80 On 'Class()', initialize 'self.data'. Argument:
81 - 'data' : optional, default 'None' --
82 - If the type of 'data' is identical to the Class' 'TYPE',
83 'data' will be shared (relevant for mutable types only).
84 - If 'data' is given (and not false), it will be converted by
85 the Class-specific method 'self._convert(data)'. Incompatible
86 data types will raise an exception.
87 - If 'data' is 'None', false, or not given, a Class-specific method
88 'self._reset()' is called to initialize an empty instance."""
89 if data:
90 if type(data) is self.TYPE:
91 self.data = data
92 else:
93 self.data = self._convert(data)
94 else:
95 self._reset()
97 def __str__(self):
98 """ On 'str(self)' and 'print self'. Returns string representation."""
99 return str(self.data)
101 def __cmp__(self, x):
102 """ On 'self>x', 'self==x', 'cmp(self,x)', etc. Catches all
103 comparisons: returns -1, 0, or 1 for less, equal, or greater."""
104 return cmp(self.data, x)
106 def __rcmp__(self, x):
107 """ On 'x>self', 'x==self', 'cmp(x,self)', etc. Catches all
108 comparisons: returns -1, 0, or 1 for less, equal, or greater."""
109 return cmp(x, self.data)
111 def __hash__(self):
112 """ On 'dictionary[self]', 'hash(self)'. Returns a unique and unchanging
113 integer hash-key."""
114 return hash(self.data)
117 class AddMulMixin:
118 """ Addition & multiplication for numbers, concatenation & repetition for
119 sequences."""
121 def __add__(self, other):
122 """ On 'self+other'. Numeric addition, or sequence concatenation."""
123 return self.data + other
125 def __radd__(self, other):
126 """ On 'other+self'. Numeric addition, or sequence concatenation."""
127 return other + self.data
129 def __mul__(self, other):
130 """ On 'self*other'. Numeric multiplication, or sequence repetition."""
131 return self.data * other
133 def __rmul__(self, other):
134 """ On 'other*self'. Numeric multiplication, or sequence repetition."""
135 return other * self.data
138 class MutableMixin:
139 """ Assignment to and deletion of collection component."""
141 def __setitem__(self, key, x):
142 """ On 'self[key]=x'."""
143 self.data[key] = x
145 def __delitem__(self, key):
146 """ On 'del self[key]'."""
147 del self.data[key]
150 class ModMixin:
151 """ Modulo remainder and string formatting."""
153 def __mod__(self, other):
154 """ On 'self%other'."""
155 return self.data % other
157 def __rmod__(self, other):
158 """ On 'other%self'."""
159 return other % self.data
162 class Number(SuperType, AddMulMixin, ModMixin):
163 """ Superclass for numeric emulation types."""
165 def __sub__(self, other):
166 """ On 'self-other'."""
167 return self.data - other
169 def __rsub__(self, other):
170 """ On 'other-self'."""
171 return other - self.data
173 def __div__(self, other):
174 """ On 'self/other'."""
175 return self.data / other
177 def __rdiv__(self, other):
178 """ On 'other/self'."""
179 return other / self.data
181 def __divmod__(self, other):
182 """ On 'divmod(self,other)'."""
183 return divmod(self.data, other)
185 def __rdivmod__(self, other):
186 """ On 'divmod(other,self)'."""
187 return divmod(other, self.data)
189 def __pow__(self, other, mod=None):
190 """ On 'pow(self,other[,mod])', 'self**other'."""
191 if mod is None:
192 return self.data ** other
193 else:
194 return pow(self.data, other, mod)
196 def __rpow__(self, other):
197 """ On 'pow(other,self)', 'other**self'."""
198 return other ** self.data
200 def __neg__(self):
201 """ On '-self'."""
202 return -self.data
204 def __pos__(self):
205 """ On '+self'."""
206 return +self.data
208 def __abs__(self):
209 """ On 'abs(self)'."""
210 return abs(self.data)
212 def __int__(self):
213 """ On 'int(self)'."""
214 return int(self.data)
216 def __long__(self):
217 """ On 'long(self)'."""
218 return long(self.data)
220 def __float__(self):
221 """ On 'float(self)'."""
222 return float(self.data)
224 def __complex__(self):
225 """ On 'complex(self)'."""
226 return complex(self.data)
228 def __nonzero__(self):
229 """ On truth-value (or uses '__len__()' if defined)."""
230 return self.data != 0
232 def __coerce__(self, other):
233 """ On mixed-type expression, 'coerce()'. Returns tuple of '(self, other)'
234 converted to a common type."""
235 return coerce(self.data, other)
238 class Integer(Number):
239 """ Emulates a Python integer."""
241 TYPE = type(1)
243 def _reset(self):
244 """ Initialize an integer."""
245 self.data = 0
247 def _convert(self, data):
248 """ Convert data into an integer."""
249 return int(data)
251 def __lshift__(self, other):
252 """ On 'self<<other'."""
253 return self.data << other
255 def __rlshift__(self, other):
256 """ On 'other<<self'."""
257 return other << self.data
259 def __rshift__(self, other):
260 """ On 'self>>other'."""
261 return self.data >> other
263 def __rrshift__(self, other):
264 """ On 'other>>self'."""
265 return other >> self.data
267 def __and__(self, other):
268 """ On 'self&other'."""
269 return self.data & other
271 def __rand__(self, other):
272 """ On 'other&self'."""
273 return other & self.data
275 def __or__(self, other):
276 """ On 'self|other'."""
277 return self.data | other
279 def __ror__(self, other):
280 """ On 'other|self'."""
281 return other | self.data
283 def __xor__(self, other):
284 """ On 'self^other'."""
285 return self.data ^ other
287 def __rxor__(self, other):
288 """ On 'other%self'."""
289 return other % self.data
291 def __invert__(self):
292 """ On '~self'."""
293 return ~self.data
295 def __oct__(self):
296 """ On 'oct(self)'. Returns octal string representation."""
297 return oct(self.data)
299 def __hex__(self):
300 """ On 'hex(self)'. Returns hexidecimal string representation."""
301 return hex(self.data)
304 class Long(Integer):
305 """ Emulates a Python long integer."""
307 TYPE = type(1L)
309 def _reset(self):
310 """ Initialize an integer."""
311 self.data = 0L
313 def _convert(self, data):
314 """ Convert data into an integer."""
315 return long(data)
318 class Float(Number):
319 """ Emulates a Python floating-point number."""
321 TYPE = type(0.1)
323 def _reset(self):
324 """ Initialize a float."""
325 self.data = 0.0
327 def _convert(self, data):
328 """ Convert data into a float."""
329 return float(data)
332 class Complex(Number):
333 """ Emulates a Python complex number."""
335 TYPE = type(0+0j)
337 def _reset(self):
338 """ Initialize an integer."""
339 self.data = 0+0j
341 def _convert(self, data):
342 """ Convert data into an integer."""
343 return complex(data)
345 def __getattr__(self, name):
346 """ On 'self.real' & 'self.imag'."""
347 if name == "real":
348 return self.data.real
349 elif name == "imag":
350 return self.data.imag
351 else:
352 raise AttributeError(name)
354 def conjugate(self):
355 """ On 'self.conjugate()'."""
356 return self.data.conjugate()
359 class Container(SuperType):
360 """ Superclass for countable, indexable collection types ('Sequence', 'Mapping')."""
362 def __len__(self):
363 """ On 'len(self)', truth-value tests. Returns sequence or mapping
364 collection size. Zero means false."""
365 return len(self.data)
367 def __getitem__(self, key):
368 """ On 'self[key]', 'x in self', 'for x in self'. Implements all
369 indexing-related operations. Membership and iteration ('in', 'for')
370 repeatedly index from 0 until 'IndexError'."""
371 return self.data[key]
374 class Sequence(Container, AddMulMixin):
375 """ Superclass for classes which emulate sequences ('List', 'Tuple', 'String')."""
377 def __getslice__(self, low, high):
378 """ On 'self[low:high]'."""
379 return self.data[low:high]
382 class String(Sequence, ModMixin):
383 """ Emulates a Python string."""
385 TYPE = type("")
387 def _reset(self):
388 """ Initialize an empty string."""
389 self.data = ""
391 def _convert(self, data):
392 """ Convert data into a string."""
393 return str(data)
396 class Tuple(Sequence):
397 """ Emulates a Python tuple."""
399 TYPE = type(())
401 def _reset(self):
402 """ Initialize an empty tuple."""
403 self.data = ()
405 def _convert(self, data):
406 """ Non-tuples cannot be converted. Raise an exception."""
407 raise TypeError("Non-tuples cannot be converted to a tuple.")
410 class MutableSequence(Sequence, MutableMixin):
411 """ Superclass for classes which emulate mutable (modifyable in-place)
412 sequences ('List')."""
414 def __setslice__(self, low, high, seq):
415 """ On 'self[low:high]=seq'."""
416 self.data[low:high] = seq
418 def __delslice__(self, low, high):
419 """ On 'del self[low:high]'."""
420 del self.data[low:high]
422 def append(self, x):
423 """ Inserts object 'x' at the end of 'self.data' in-place."""
424 self.data.append(x)
426 def count(self, x):
427 """ Returns the number of occurrences of 'x' in 'self.data'."""
428 return self.data.count(x)
430 def extend(self, x):
431 """ Concatenates sequence 'x' to the end of 'self' in-place
432 (like 'self=self+x')."""
433 self.data.extend(x)
435 def index(self, x):
436 """ Returns the offset of the first occurrence of object 'x' in
437 'self.data'; raises an exception if not found."""
438 return self.data.index(x)
440 def insert(self, i, x):
441 """ Inserts object 'x' into 'self.data' at offset 'i'
442 (like 'self[i:i]=[x]')."""
443 self.data.insert(i, x)
445 def pop(self, i=-1):
446 """ Returns and deletes the last item of 'self.data' (or item
447 'self.data[i]' if 'i' given)."""
448 return self.data.pop(i)
450 def remove(self, x):
451 """ Deletes the first occurrence of object 'x' from 'self.data';
452 raise an exception if not found."""
453 self.data.remove(x)
455 def reverse(self):
456 """ Reverses items in 'self.data' in-place."""
457 self.data.reverse()
459 def sort(self, func=None):
461 Sorts 'self.data' in-place. Argument:
462 - func : optional, default 'None' --
463 - If 'func' not given, sorting will be in ascending
464 order.
465 - If 'func' given, it will determine the sort order.
466 'func' must be a two-argument comparison function
467 which returns -1, 0, or 1, to mean before, same,
468 or after ordering."""
469 if func:
470 self.data.sort(func)
471 else:
472 self.data.sort()
475 class List(MutableSequence):
476 """ Emulates a Python list. When instantiating an object with data
477 ('List(data)'), you can force a copy with 'List(list(data))'."""
479 TYPE = type([])
481 def _reset(self):
482 """ Initialize an empty list."""
483 self.data = []
485 def _convert(self, data):
486 """ Convert data into a list."""
487 return list(data)
490 class Mapping(Container):
491 """ Superclass for classes which emulate mappings/hashes ('Dictionary')."""
493 def has_key(self, key):
494 """ Returns 1 (true) if 'self.data' has a key 'key', or 0 otherwise."""
495 return self.data.has_key(key)
497 def keys(self):
498 """ Returns a new list holding all keys from 'self.data'."""
499 return self.data.keys()
501 def values(self):
502 """ Returns a new list holding all values from 'self.data'."""
503 return self.data.values()
505 def items(self):
506 """ Returns a new list of tuple pairs '(key, value)', one for each entry
507 in 'self.data'."""
508 return self.data.items()
510 def clear(self):
511 """ Removes all items from 'self.data'."""
512 self.data.clear()
514 def get(self, key, default=None):
515 """ Similar to 'self[key]', but returns 'default' (or 'None') instead of
516 raising an exception when 'key' is not found in 'self.data'."""
517 return self.data.get(key, default)
519 def copy(self):
520 """ Returns a shallow (top-level) copy of 'self.data'."""
521 return self.data.copy()
523 def update(self, dict):
524 """ Merges 'dict' into 'self.data'
525 (i.e., 'for (k,v) in dict.items(): self.data[k]=v')."""
526 self.data.update(dict)
529 class Dictionary(Mapping, MutableMixin):
530 """ Emulates a Python dictionary, a mutable mapping. When instantiating an
531 object with data ('Dictionary(data)'), you can force a (shallow) copy
532 with 'Dictionary(data.copy())'."""
534 TYPE = type({})
536 def _reset(self):
537 """ Initialize an empty dictionary."""
538 self.data = {}
540 def _convert(self, data):
541 """ Non-dictionaries cannot be converted. Raise an exception."""
542 raise TypeError("Non-dictionaries cannot be converted to a dictionary.")
545 if __name__ == "__main__":
546 print __doc__ # show module's documentation string