A list that will only allow strings as members. Methods like count, __contains__ ( a in list ), remove, index etc are case insensitive. See also the caselessDict - http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/283455
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 | # 17-05-04
# v1.1.1
#
# caselessList
# A case insensitive list that only permits strings as keys.
# Implemented for ConfigObj
# Requires Python 2.2 or above
# Copyright Michael Foord
# Not for use in commercial projects without permission. (Although permission will probably be given).
# If you use in a non-commercial project then please credit me and include a link back.
# If you release the project non-commercially then let me know (and include this message with my code !)
# No warranty express or implied for the accuracy, fitness to purpose or otherwise for this code....
# Use at your own risk !!!
# E-mail fuzzyman AT atlantibots DOT org DOT uk (or michael AT foord DOT me DO
class caselessList(list):
"""A case insensitive lists that has some caseless methods. Only allows strings as list members.
Most methods that would normally return a list, return a caselessList. (Except list() and lowercopy())
Sequence Methods implemented are :
__contains__, remove, count, index, append, extend, insert,
__getitem__, __setitem__, __getslice__, __setslice__
__add__, __radd__, __iadd__, __mul__, __rmul__
Plus Extra methods:
findentry, copy , lowercopy, list
Inherited methods :
__imul__, __len__, __iter__, pop, reverse, sort
"""
def __init__(self, inlist=[]):
list.__init__(self)
for entry in inlist:
if not isinstance(entry, str): raise TypeError('Members of this object must be strings. You supplied \"%s\" which is \"%s\"' % (entry, type(entry)))
self.append(entry)
def findentry(self, item):
"""A caseless way of checking if an item is in the list or not.
It returns None or the entry."""
if not isinstance(item, str): raise TypeError('Members of this object must be strings. You supplied \"%s\"' % type(item))
for entry in self:
if item.lower() == entry.lower(): return entry
return None
def __contains__(self, item):
"""A caseless way of checking if a list has a member in it or not."""
for entry in self:
if item.lower() == entry.lower(): return True
return False
def remove(self, item):
"""Remove the first occurence of an item, the caseless way."""
for entry in self:
if item.lower() == entry.lower():
list.remove(self, entry)
return
raise ValueError(': list.remove(x): x not in list')
def copy(self):
"""Return a caselessList copy of self."""
return caselessList(self)
def list(self):
"""Return a normal list version of self."""
return list(self)
def lowercopy(self):
"""Return a lowercase (list) copy of self."""
return [entry.lower() for entry in self]
def append(self, item):
"""Adds an item to the list and checks it's a string."""
if not isinstance(item, str): raise TypeError('Members of this object must be strings. You supplied \"%s\"' % type(item))
list.append(self, item)
def extend(self, item):
"""Extend the list with another list. Each member of the list must be a string."""
if not isinstance(item, list): raise TypeError('You can only extend lists with lists. You supplied \"%s\"' % type(item))
for entry in item:
if not isinstance(entry, str): raise TypeError('Members of this object must be strings. You supplied \"%s\"' % type(entry))
list.append(self, entry)
def count(self, item):
"""Counts references to 'item' in a caseless manner.
If item is not a string it will always return 0."""
if not isinstance(item, str): return 0
count = 0
for entry in self:
if item.lower() == entry.lower():
count += 1
return count
def index(self, item, minindex=0, maxindex=None):
"""Provide an index of first occurence of item in the list. (or raise a ValueError if item not present)
If item is not a string, will raise a TypeError.
minindex and maxindex are also optional arguments
s.index(x[, i[, j]]) return smallest k such that s[k] == x and i <= k < j
"""
if maxindex == None: maxindex = len(self)
minindex = max(0, minindex)-1
maxindex = min(len(self), maxindex)
if not isinstance(item, str): raise TypeError('Members of this object must be strings. You supplied \"%s\"' % type(item))
index = minindex
while index < maxindex:
index += 1
if item.lower() == self[index].lower():
return index
raise ValueError(': list.index(x): x not in list')
def insert(self, i, x):
"""s.insert(i, x) same as s[i:i] = [x]
Raises TypeError if x isn't a string."""
if not isinstance(x, str): raise TypeError('Members of this object must be strings. You supplied \"%s\"' % type(x))
list.insert(self, i, x)
def __setitem__(self, index, value):
"""For setting values in the list.
index must be an integer or (extended) slice object. (__setslice__ used for simple slices)
If index is an integer then value must be a string.
If index is a slice object then value must be a list of strings - with the same length as the slice object requires.
"""
if isinstance(index, int):
if not isinstance(value, str): raise TypeError('Members of this object must be strings. You supplied \"%s\"' % type(value))
list.__setitem__(self, index, value)
elif isinstance(index, slice):
if not hasattr(value, '__len__'): raise TypeError('Value given to set slice is not a sequence object.')
for entry in value:
if not isinstance(entry, str): raise TypeError('Members of this object must be strings. You supplied \"%s\"' % type(entry))
list.__setitem__(self, index, value)
else:
raise TypeError('Indexes must be integers or slice objects.')
def __setslice__(self, i, j, sequence):
"""Called to implement assignment to self[i:j]."""
for entry in sequence:
if not isinstance(entry, str): raise TypeError('Members of this object must be strings. You supplied \"%s\"' % type(entry))
list.__setslice__(self, i, j, sequence)
def __getslice__(self, i, j):
"""Called to implement evaluation of self[i:j].
Although the manual says this method is deprecated - if I don't define it the list one is called.
(Which returns a list - this returns a caselessList)"""
return caselessList(list.__getslice__(self, i, j))
def __getitem__(self, index):
"""For fetching indexes.
If a slice is fetched then the list returned is a caselessList."""
if not isinstance(index, slice):
return list.__getitem__(self, index)
else:
return caselessList(list.__getitem__(self, index))
def __add__(self, item):
"""To add a list, and return a caselessList.
Every element of item must be a string."""
return caselessList(list.__add__(self, item))
def __radd__(self, item):
"""To add a list, and return a caselessList.
Every element of item must be a string."""
return caselessList(list.__add__(self, item))
def __iadd__(self, item):
"""To add a list in place."""
for entry in item: self.append(entry)
def __mul__(self, item):
"""To multiply itself, and return a caselessList.
Every element of item must be a string."""
return caselessList(list.__mul__(self, item))
def __rmul__(self, item):
"""To multiply itself, and return a caselessList.
Every element of item must be a string."""
return caselessList(list.__rmul__(self, item))
####################################################################################
# brief test stuff
if __name__ == '__main__':
print
print 'caselessList Tests :'
a = caselessList(['hello', 'HELLO', 'HellO'])
print 'A caselessList : ', a
print 'a.findentry(\'hELLO\') = ', a.findentry('hELLO')
print '(prints the first entry that matches this)', '\n'
print '\'HeLLo\' in a : ', 'HeLLo' in a, '\n' # tests __contains__
a.remove('HeLlO')
print 'a.remove(\'HeLlO\'), print a : ', a
print 'type(a.copy()) : ', type(a.copy())
print 'type(a.list()) : ', type(a.list())
print 'a.lowercopy() : ', a.lowercopy()
a.append('HeLlO')
print 'a.append(\'HeLlO\'), print a : ', a
a.extend([char for char in 'AaAaA'])
print 'a.extend([char for char in \'AaAaA\']), print a, type(a) : '
print a, ',', type(a)
print 'a.count(\'A\') : ', a.count('A')
print 'a.index(\'A\') : ', a.index('a')
a.insert(1, 'WisH')
print 'a.insert(1, \'WisH\') : ',a
print
print 'The __setitem__ method is only novel for extended slice operations.'
a[0:10:3] = ['Fish', 'fIsh', 'fiSh']
print "a[0:10:3] = ['Fish', 'fIsh', 'fiSh'] : ", a
print
print 'Most interesting thing about __getitem__ is that if you ask for a slice - it will be an instance of caselessList'
print 'type(a[0:4:1]) : ', type(a[0:4:1])
"""
15-05-04 Version 1.1.0
Added caselessList a caseless List implementation.
Lot more work than dict actually - more methods to implement for a sequence object.
Changed module name from caselessDict to caseless.
"""
|
In parsing keywords from a config file that is created by a user I wanted to check if a keyword was already in a list.
In order to simplify things for the user I wanted to make the keywords case insensitive....... So I thought a caseless list would be a good companion to the caselessDict - http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/283455 It was actually harder to implement than caselessDict as sequences have more methods that might set values.
Example :
>>> a = caselessList(['fIsH', 'fish', 'FISH'])
>>> 'FiSh' in a
True
>>> a.count('FISh')
3
See ConfigObj at http://www.voidspace.org.uk/atlantibots/pythonutils.html
A more flexible way of doing this. Another way of achieving the same effect is to use a case insensitive string (much less code :-) and a normal list or dict. See recipe: