2007-06-29

 

Python Cookbook 1.24 让字符串大小写不敏感

需求:

要处理一个字符串,需要让它对大小写不敏感,如字符串查找和比较等.而其它的字符串依然保持原来的特性.

讨论:

最好的办法就是从str继承一个子类来处理.

class iStr(str):
    """
    Case insensitive string class.
    Behaves just like str, except that all comparisons and lookups
    are case insensitive.
    """
    def _ _init_ _(self, *args):
        self._lowered = str.lower(self)
    def _ _repr_ _(self):
        return '%s(%s)' % (type(self)._ _name_ _, str._ _repr_ _(self))
    def _ _hash_ _(self):
        return hash(self._lowered)
    def lower(self):
        return self._lowered
def _make_case_insensitive(name):
    ''' wrap one method of str into an iStr one, case-insensitive '''
    str_meth = getattr(str, name)
    def x(self, other, *args):
        ''' try lowercasing 'other', which is typically a string, but
            be prepared to use it as-is if lowering gives problems,
            since strings CAN be correctly compared with non-strings.
        '''
        try: other = other.lower( )
        except (TypeError, AttributeError, ValueError): pass
        return str_meth(self._lowered, other, *args)
    # in Python 2.4, only, add the statement: x.func_name = name
    setattr(iStr, name, x)
# apply the _make_case_insensitive function to specified methods
for name in 'eq lt le gt gt ne cmp contains'.split( ):
    _make_case_insensitive('_ _%s_ _' % name)
for name in 'count endswith find index rfind rindex startswith'.split( ):
    _make_case_insensitive(name)
# note that we don't modify methods 'replace', 'split', 'strip', ...
# of course, you can add modifications to them, too, if you prefer that.
del _make_case_insensitive    # remove helper function, not needed any more

在类iStr中有一些要点需要注意.首先,我们在初始化的时候生成了需要处理字符串的小写版本,在__init__方法中这样做,是因为iStr对象要重复使用, 这样处理可以提高效率.我们使用一个下划线来标识这个变量,而不是两个,让它对子类可见,对外部不可见.
在这里并没有提供有些方法的大小写不敏感版本,因为很难在一般情况下确定具体的需求.在具体的应用中,子类可以根据自己的需求做特定的处理.举例说明,如果子类写了一个针对iStr的replace方法,而它的返回值是str类型,如果想要让所有的返回值也是iStr类型,需要写一个助手函数来处理:

def _make_return_iStr(name):
    str_meth = getattr(str, name)
    def x(*args):
        return iStr(str_meth(*args))
    setattr(iStr, name, x)

然后你需要针对特定的字符串方法调用这个函数:

for name in 'center ljust rjust strip lstrip rstrip'.split( ):
    _make_return_iStr(name)

String大约有20个相关方法(包括__add__和__mul__),有些是需要你封装的.如果你想封装更多的方法,要进行更多的处理,比如split和join,或者encode和decode,你需要从unicode继承子类,而不是str.
在上例中,我们尽量避免使用模板代码.只需要在特定应用的时候重写iStr的方法就可以了.自己重写一个类,也不会带来更多的好处.
在Python2.4以及以后的版本,你可以将一个方法赋值给一个函数对象.这样,就可以获得更好的可读性较高的代码,尤其是在函数叠加使用的时候.在Python2.3中,函数对象是只读的,在这里我们避免改变函数对象的值,已防止兼容性的问题.
大小写无关(并不改变大小写)有很多用处,比如在检查用户输入,或者有些文件系统上(windows和Macintosh), 文件名都是不区分大小写的.你可以很容易的为自己创建大小写无关的列表,字典,集合等.
举例说明,一个列表里面保存了一些字符串,而需要用一些大小写无关的方法来处理他们,如排序,索引等.可以构造iStr来处理它们:

class iList(list):
    def _ _init_ _(self, *args):
        list._ _init_ _(self, *args)
        # rely on _ _setitem_ _ to wrap each item into iStr...
        self[:] = self
    wrap_each_item = iStr
    def _ _setitem_ _(self, i, v):
        if isinstance(i, slice): v = map( self.wrap_each_item, v)
        else: v = self.wrap_each_item(v)
        list._ _setitem_ _(self, i, v)
    def append(self, item):
        list.append(self, self.wrap_each_item(item))
    def extend(self, seq):
        list.extend(self, map(self.wrap_each_item, seq))

基本上,我们确保了iList中的每一个对象都被iStr封装了.

标签:


Comments: 发表评论



<< Home

This page is powered by Blogger. Isn't yours?