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

简介:

本节书摘来自异步社区《Python Cookbook(第2版)中文版》一书中的第1章,第1.24节,作者[美]Alex Martelli , Anna Martelli Ravenscrof , David Ascher ,高铁军 译,更多章节内容可以访问云栖社区“异步社区”公众号查看。

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即可。

相关文章
|
1月前
|
Python
Python中的f-string:更优雅的字符串格式化
Python中的f-string:更优雅的字符串格式化
214 100
|
1月前
|
开发者 Python
Python中的f-string:高效字符串格式化的利器
Python中的f-string:高效字符串格式化的利器
284 99
|
1月前
|
Python
Python中的f-string:更优雅的字符串格式化
Python中的f-string:更优雅的字符串格式化
|
1月前
|
开发者 Python
Python f-strings:更优雅的字符串格式化技巧
Python f-strings:更优雅的字符串格式化技巧
|
1月前
|
开发者 Python
Python f-string:高效字符串格式化的艺术
Python f-string:高效字符串格式化的艺术
|
1月前
|
Python
使用Python f-strings实现更优雅的字符串格式化
使用Python f-strings实现更优雅的字符串格式化
|
12天前
|
存储 Java 索引
(Python基础)新时代语言!一起学习Python吧!(二):字符编码由来;Python字符串、字符串格式化;list集合和tuple元组区别
字符编码 我们要清楚,计算机最开始的表达都是由二进制而来 我们要想通过二进制来表示我们熟知的字符看看以下的变化 例如: 1 的二进制编码为 0000 0001 我们通过A这个字符,让其在计算机内部存储(现如今,A 字符在地址通常表示为65) 现在拿A举例: 在计算机内部 A字符,它本身表示为 65这个数,在计算机底层会转为二进制码 也意味着A字符在底层表示为 1000001 通过这样的字符表示进行转换,逐步发展为拥有127个字符的编码存储到计算机中,这个编码表也被称为ASCII编码。 但随时代变迁,ASCII编码逐渐暴露短板,全球有上百种语言,光是ASCII编码并不能够满足需求
78 3
|
1月前
|
数据采集 机器学习/深度学习 人工智能
Python:现代编程的首选语言
Python:现代编程的首选语言
203 102
|
1月前
|
数据采集 机器学习/深度学习 算法框架/工具
Python:现代编程的瑞士军刀
Python:现代编程的瑞士军刀
213 104
|
1月前
|
人工智能 自然语言处理 算法框架/工具
Python:现代编程的首选语言
Python:现代编程的首选语言
195 103

热门文章

最新文章

  • 1
    Python零基础爬取东方财富网股票行情数据指南
    214
  • 2
    解析Python爬虫中的Cookies和Session管理
    166
  • 3
    Python日志模块配置:从print到logging的优雅升级指南
    123
  • 4
    【可视化大屏】全流程讲解用python的pyecharts库实现拖拽可视化大屏的背后原理,简单粗暴!
    92
  • 5
    (Pandas)Python做数据处理必选框架之一!(二):附带案例分析;刨析DataFrame结构和其属性;学会访问具体元素;判断元素是否存在;元素求和、求标准值、方差、去重、删除、排序...
    106
  • 6
    (Pandas)Python做数据处理必选框架之一!(一):介绍Pandas中的两个数据结构;刨析Series:如何访问数据;数据去重、取众数、总和、标准差、方差、平均值等;判断缺失值、获取索引...
    200
  • 7
    (numpy)Python做数据处理必备框架!(二):ndarray切片的使用与运算;常见的ndarray函数:平方根、正余弦、自然对数、指数、幂等运算;统计函数:方差、均值、极差;比较函数...
    75
  • 8
    (numpy)Python做数据处理必备框架!(一):认识numpy;从概念层面开始学习ndarray数组:形状、数组转置、数值范围、矩阵...
    202
  • 9
    (Python基础)新时代语言!一起学习Python吧!(四):dict字典和set类型;切片类型、列表生成式;map和reduce迭代器;filter过滤函数、sorted排序函数;lambda函数
    59
  • 10
    (Python基础)新时代语言!一起学习Python吧!(三):IF条件判断和match匹配;Python中的循环:for...in、while循环;循环操作关键字;Python函数使用方法
    92
  • 推荐镜像

    更多