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

相关文章
|
3月前
|
Python
在 Python 中,如何将日期时间类型转换为字符串?
在 Python 中,如何将日期时间类型转换为字符串?
148 64
|
30天前
|
存储 人工智能 Python
[oeasy]python061_如何接收输入_input函数_字符串_str_容器_ 输入输出
本文介绍了Python中如何使用`input()`函数接收用户输入。`input()`函数可以从标准输入流获取字符串,并将其赋值给变量。通过键盘输入的值可以实时赋予变量,实现动态输入。为了更好地理解其用法,文中通过实例演示了如何接收用户输入并存储在变量中,还介绍了`input()`函数的参数`prompt`,用于提供输入提示信息。最后总结了`input()`函数的核心功能及其应用场景。更多内容可参考蓝桥、GitHub和Gitee上的相关教程。
16 0
|
2月前
|
存储 测试技术 Python
Python 中别再用 ‘+‘ 拼接字符串了!
通过选择合适的字符串拼接方法,可以显著提升 Python 代码的效率和可读性。在实际开发中,根据具体需求和场景选择最佳的方法,避免不必要的性能损失。
62 5
|
2月前
|
Python
使用Python计算字符串的SHA-256散列值
使用Python计算字符串的SHA-256散列值
72 7
|
3月前
|
Python
在 Python 中,如何将字符串中的日期格式转换为日期时间类型?
在 Python 中,如何将字符串中的日期格式转换为日期时间类型?
61 6
|
4月前
|
Python
【10月更文挑战第6天】「Mac上学Python 11」基础篇5 - 字符串类型详解
本篇将详细介绍Python中的字符串类型及其常见操作,包括字符串的定义、转义字符的使用、字符串的连接与格式化、字符串的重复和切片、不可变性、编码与解码以及常用内置方法等。通过本篇学习,用户将掌握字符串的操作技巧,并能灵活处理文本数据。
70 1
【10月更文挑战第6天】「Mac上学Python 11」基础篇5 - 字符串类型详解
|
4月前
|
存储 Python
[oeasy]python038_ range函数_大小写字母的起止范围_start_stop
本文介绍了Python中`range`函数的使用方法及其在生成大小写字母序号范围时的应用。通过示例展示了如何利用`range`和`for`循环输出指定范围内的数字,重点讲解了小写和大写字母对应的ASCII码值范围,并解释了`range`函数的参数(start, stop)以及为何不包括stop值的原因。最后,文章留下了关于为何`range`不包含stop值的问题,留待下一次讨论。
52 1
|
4月前
|
自然语言处理 Java 数据处理
【速收藏】python字符串操作,你会几个?
【速收藏】python字符串操作,你会几个?
94 7
|
2月前
|
人工智能 数据可视化 数据挖掘
探索Python编程:从基础到高级
在这篇文章中,我们将一起深入探索Python编程的世界。无论你是初学者还是有经验的程序员,都可以从中获得新的知识和技能。我们将从Python的基础语法开始,然后逐步过渡到更复杂的主题,如面向对象编程、异常处理和模块使用。最后,我们将通过一些实际的代码示例,来展示如何应用这些知识解决实际问题。让我们一起开启Python编程的旅程吧!

热门文章

最新文章