1 #!/usr/local/bin/python
3 # typelib.py (Type Class Library)
4 # Copyright (c) 2000 David John Goodger
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.
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.
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.
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."""
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.
66 # - Implemented String, Tuple, List, and Dictionary emulation.
69 # - Implement Function? File? (Have to come up with a good reason first ;-)
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):
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."""
90 if type(data
) is self
.TYPE
:
93 self
.data
= self
._convert
(data
)
98 """ On 'str(self)' and 'print self'. Returns string representation."""
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
)
112 """ On 'dictionary[self]', 'hash(self)'. Returns a unique and unchanging
114 return hash(self
.data
)
118 """ Addition & multiplication for numbers, concatenation & repetition for
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
139 """ Assignment to and deletion of collection component."""
141 def __setitem__(self
, key
, x
):
142 """ On 'self[key]=x'."""
145 def __delitem__(self
, key
):
146 """ On 'del self[key]'."""
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'."""
192 return self
.data
** other
194 return pow(self
.data
, other
, mod
)
196 def __rpow__(self
, other
):
197 """ On 'pow(other,self)', 'other**self'."""
198 return other
** self
.data
209 """ On 'abs(self)'."""
210 return abs(self
.data
)
213 """ On 'int(self)'."""
214 return int(self
.data
)
217 """ On 'long(self)'."""
218 return long(self
.data
)
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."""
244 """ Initialize an integer."""
247 def _convert(self
, data
):
248 """ Convert data into an integer."""
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
):
296 """ On 'oct(self)'. Returns octal string representation."""
297 return oct(self
.data
)
300 """ On 'hex(self)'. Returns hexidecimal string representation."""
301 return hex(self
.data
)
305 """ Emulates a Python long integer."""
310 """ Initialize an integer."""
313 def _convert(self
, data
):
314 """ Convert data into an integer."""
319 """ Emulates a Python floating-point number."""
324 """ Initialize a float."""
327 def _convert(self
, data
):
328 """ Convert data into a float."""
332 class Complex(Number
):
333 """ Emulates a Python complex number."""
338 """ Initialize an integer."""
341 def _convert(self
, data
):
342 """ Convert data into an integer."""
345 def __getattr__(self
, name
):
346 """ On 'self.real' & 'self.imag'."""
348 return self
.data
.real
350 return self
.data
.imag
352 raise AttributeError(name
)
355 """ On 'self.conjugate()'."""
356 return self
.data
.conjugate()
359 class Container(SuperType
):
360 """ Superclass for countable, indexable collection types ('Sequence', 'Mapping')."""
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."""
388 """ Initialize an empty string."""
391 def _convert(self
, data
):
392 """ Convert data into a string."""
396 class Tuple(Sequence
):
397 """ Emulates a Python tuple."""
402 """ Initialize an empty tuple."""
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
]
423 """ Inserts object 'x' at the end of 'self.data' in-place."""
427 """ Returns the number of occurrences of 'x' in 'self.data'."""
428 return self
.data
.count(x
)
431 """ Concatenates sequence 'x' to the end of 'self' in-place
432 (like 'self=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
)
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
)
451 """ Deletes the first occurrence of object 'x' from 'self.data';
452 raise an exception if not found."""
456 """ Reverses items in 'self.data' in-place."""
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
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."""
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))'."""
482 """ Initialize an empty list."""
485 def _convert(self
, data
):
486 """ Convert data into a list."""
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
)
498 """ Returns a new list holding all keys from 'self.data'."""
499 return self
.data
.keys()
502 """ Returns a new list holding all values from 'self.data'."""
503 return self
.data
.values()
506 """ Returns a new list of tuple pairs '(key, value)', one for each entry
508 return self
.data
.items()
511 """ Removes all items from 'self.data'."""
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
)
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())'."""
537 """ Initialize an empty dictionary."""
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