《Python Cookbook(第2版)中文版》——1.24 让某些字符串大小写不敏感

1.24 让某些字符串大小写不敏感

任务

你想让某些字符串在比较和查询的时候是大小写不敏感的,但在其他操作中却保持原状。

解决方案

最好的解决方式是,将这种字符串封装在str的一个合适的子类中:

class iStr(str):
      """
      大小写不敏感的字符串类
      行为方式类似于str,只是所有的比较和查询
      都是大小写不敏感的
      """
      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):
    ''' 将str的方法封装成iStr的方法,大小写不敏感 '''
    str_meth = getattr(str, name)
    def x(self, other, *args):
         ''' 先尝试将other小写化,通常这应该是一个字符串,
             但必须要做好准备应对这个过程中出现的错误,
             因为字符串是可以和非字符串正确地比较的
         '''
         try: other = other.lower( )
         except (TypeError, AttributeError, ValueError): pass
         return str_meth(self._lowered, other, *args)
    # 仅Python 2.4,增加一条语句:x.func_name = name
    setattr(iStr, name, x)
# 将_make_case_insensitive函数应用于指定的方法
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)
# 注意,我们并不修改replace、split、strip等方法
# 当然,如果有需要,也可以对它们进行修改
del _make_case_insensitive    # 删除帮助函数,已经不再需要了

讨论

iStr类的一些实现上的选择很值得讨论。首先,我们在 _init _中一次性生成了小写版本,这是因为我们认识到在iStr的典型应用中,这个小写版本将会被反复地使用。我们在一个私有的变量中保存这个小写版本,将其作为一个属性,当然,也别保护得太过分了(它以一个下划线开头,而不是两个下划线),因为如果从iStr再派生子类(比如,进一步对其扩展,支持大小写不敏感的切分和替换等,正如“解决方案”注释中所说的),iStr的子类很有可能会需要访问其父类iStr的一些关键的“实现细节”。

这里我们没有提供其他一些方法的大小写不敏感的版本,如replace,因为这个例子已经清晰地展示了一种通用的建立输入和输出之间联系的方式。根据应用进行特别定制的子类将提供最能够满足需求的功能。比如,replace方法并没有被封装,则我们对一个iStr的实例调用replace,返回的是str的实例,而不是iStr。如果这会给你的应用带来问题,可以将所有的返回字符串的iStr方法封装起来,这就可以确保所有返回的结果是iStr的实例。基于这个目的,需要另一个单独的助手函数,相似但不完全等同于解决方案中给出的_make_case_insensitive:

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

需要对所有返回字符串的方法的名字应用这个助手函数,_make_return_iStr:

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

字符串有约20种方法(包括一些特殊方法,比如 _add 和 mul _),需要考虑哪些方法应该被封装起来。也可以把一些额外的方法,比如split和join(它们可能需要一些特别的处理)封装起来,或者其他的方法,如encode和decode,对于此类方法,除非定义了一个大小写不敏感的unicode子类型,否则无法处理它们。而实际上,针对一个特定的应用,可能不是所有的未封装的方法都会引起问题。正如你所见的那样,由于Python字符串的方法和功能很丰富,要想用一种通用的不依赖于特定应用的方式,完全彻底地定制出一个子类型,还是要花点功夫的。

iStr的实现很谨慎,主要是为了避免一些重复性的例行公事般的代码(通常是冗长且容易滋生bug的代码),如果我们用普通的方式重载str每一个需要的方法,在类的实现中写上一堆def语句,很有可能就会陷入这种尴尬的境地。使用可自定义的元类或者其他的高级技术对这个例子而言也不会有什么特别的优势,但使用一个辅助函数来生成和安装封装层闭包,就可以轻易地避开问题。然后我们在两个循环中使用该辅助函数,一个循环处理常用的方法,另一个则处理特殊的方法。这两个循环都必须被放置在class语句之后,正如我们在解决方案中给出的代码所示,这是因为这两个循环需要修改iStr类对象,但除非用class语句完成对iStr类的声明,否则那个类对象根本就不存在(因此当然也无法修改)。

在Python 2.4中,可以重新指定函数对象的func_name属性,在本例中,当对iStr实例应用内省机制时,可以用这种方法让代码变得更加清晰和易读。但在Python 2.3中,函数对象的func_name属性是只读的。因此,在本节的讨论中,我们仅仅是指出了另一种可能性,我们不想因为这个小问题失去对Python 2.3的兼容性。

大小写不敏感(但仍保留了大小写信息)的字符串有很多用途,包括提高对用户输入进行解析的宽松度,在文件系统(比如Windows和Macintosh的文件系统)中查找名字包含指定字符的文件,等等。你可能会发现,有很多地方需要“大小写不敏感”的容器类型,比如字典、列表、集合等—它们都需要在某些场合,忽略掉key或者子项的大小写的信息。很明显,一个好的方法是一次性构建出“大小写不敏感”的比较和查询功能;现在你的工具箱中已经增加了本节提供的解决方案,可以对字符串进行任何需要的封装和定制,你甚至还能定制其他一些你希望具备“大小写不敏感”能力的容器类型。

比如,一个所有子项都是字符串的列表,你希望能够进行一些大小写无关的处理(如用count和index进行排序),完全可以基于iStr的实现,轻易地构建一个iList:

class iList(list):
       def _ _init_ _(self, *args):
             list._ _init_ _(self, *args)
             # 依赖_ _setitem_ _将各项封装为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来封装,其余部分则保持原状。

另外提一句,iList的实现方式使得可以根据应用提供特定的iStr,轻易地完成对子类的定制:只需在iList的子类中重载成员变量wrap_each_item即可。

时间: 2024-10-23 00:51:13

《Python Cookbook(第2版)中文版》——1.24 让某些字符串大小写不敏感的相关文章

《“笨办法”学Python(第3版)》——习题6 字符串和文本

习题6 字符串和文本 虽然你已经在程序中写过字符串了,但是你还不了解它们的用处.在这个习题中我们将使用复杂的字符串来建立一系列变量,从中你将学到它们的用途.首先,我们解释一下字符串是什么. 字符串通常是指你想要展示给别人的或者是想要从程序里"导出"的一小段字符.Python可以通过文本里的双引号(")或者单引号(')识别出字符串来.这在以前的打印练习中你已经见过很多次了.如果你把单引号或者双引号括起来的文本放到print后面,它们就会被Python打印出来. 字符串可以包含之

《Python Cookbook(第2版)中文版》——导读

前 言 这本书不是一本典型的O'Reilly风格的书,而是一本集合了多个作者的手稿的作品.实际上,这也是一种将开源开发的方式应用到书籍出版业的尝试.Python社区有超过300个成员在本书中贡献了他们的心得和资料.在这里,我们作为编辑,想给你--本书的读者,介绍一些重要的背景资料,这些背景资料是关于此书是如何编著出来,以及这个过程和涉及的人,并提出一些关于这种崭新的风格的思考. 目 录 [第1章 文本1.1 每次处理一个字符](https://yq.aliyun.com/articles/963

《Python Cookbook(第2版)中文版》——1.21 在Unicode和普通字符串之间转换

1.21 在Unicode和普通字符串之间转换 任务 需要处理一些可能不符合ASCII字符集的文本数据. 解决方案 普通字符串可以用多种方式编码成Unicode字符串,具体要看你究竟选择了哪种编码: unicodestring = u"Hello world" # 将Unicode转化为普通Python字符串:"encode" utf8string = unicodestring.encode("utf-8") asciistring = uni

《Python Cookbook(第2版)中文版》——1.10 过滤字符串中不属于指定集合的字符

1.10 过滤字符串中不属于指定集合的字符 任务 给定一个需要保留的字符的集合,构建一个过滤函数,并可将其应用于任何字符串s,函数返回一个s的拷贝,该拷贝只包含指定字符集合中的元素. 解决方案 对于此类问题,string对象的translate方法是又快又好用的工具.不过,为了有效地使用translate来解决问题,事先我们必须做一些准备工作.传递给translate的第一个参数是一个翻译表:在本节中,我们其实不需要什么翻译,所以我们必须准备一个特制的参数来指明"无须翻译".第二个参数

《Python Cookbook(第2版)中文版》——第1章 文本 1.1 每次处理一个字符

第1章 文本 引言 感谢:Fred L. Drake, Jr.,PythonLabs 对于脚本语言来说,文本处理任务构成了一个重要的组成部分,每个人都会同意文本处理非常有用.每个人都会有一些文本需要重新格式化或者转化为另一种形式.问题是,每个程序都与另一个程序有点不同,无论它们是多么相似,想提取出一些可复用的代码片段并用它来处理不同的文件格式仍然是非常困难的. 什么是文本 看起来问题有点简单得过分了,事实上,我们看到了文本,就知道了什么是文本,文本是一串字符,这正是它与二进制数据之间的不同.二进

《Python 3程序开发指南(第2版•修订版)》——2.4 字符串

2.4 字符串 字符串是使用固定不变的str数据类型表示的,其中存放Unicode字符序列.str数据类型可以作为函数进行调用,用于创建字符串对象--参数为空时返回一个空字符串,参数为非字符串类型时返回该参数的字符串形式,参数为字符串时返回该字符串的拷贝.str()函数也可以用作一个转换函数,此时要求第一个参数为字符串或可以转换为字符串的其他数据类型,其后跟随至多两个可选的字符串参数,其中一个用于指定要使用的编码格式,另一个用于指定如何处理编码错误. 前面我们注意到,字符串是使用引号创建的,可以

谁有<<CLR Via C#>>第三版中文版的电子书

问题描述 谁有<<CLRViaC#>>第三版中文版的电子书,我是个初学者,看网上推荐此书的人多,想看一下,我的QQ:330784617.谢谢!! 解决方案 解决方案二:试一试我一般看英文的,虽然很少看书:(解决方案三: 解决方案四:第二版有的,想看第三版.

拒绝从入门到放弃_《Python 核心编程 (第二版)》必读目录

目录 目录 关于这本书 必看知识点 最后 关于这本书 <Python 核心编程 (第二版)>是一本 Python 编程的入门书,分为 Python 核心(其实并不核心,应该叫基础) 和 高级主题 两大部分,以 Python 2.x 作为主要演示版本,涵盖的知识面广,知识点较齐全,代码多且好理解,但对 Python 版本特性的内容太久远,不合时宜. 整体来说 Python 核心 部分是主要内容,高级主题 部分作为应用扩展内容.后半部分篇幅较短,内容不够深入,只到了解的层面,好在横向够广(每一个主

(六十二)第四章总结——《C++ Primer Plus 第6版 中文版》

书是<C++ Primer Plus  第6版  中文版> 数组.指针.结构 是C++的3种复合类型.   注:为了方便,类型名用int为主,变量名用a为主.   数组: 包括数组(例如int a[10];)和字符串(例如char a[10];),还有string类(例如string a="abc";),vector类(例如vector<int>a(5)).array类(array<int,3>a)等. 数组名表示数组所在的(第一个元素)内存地址.