《Python Cookbook(第2版)中文版》——1.18 一次完成多个替换

简介:

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

1.18 一次完成多个替换

任务

你想对字符串的某些子串进行替换。

解决方案

正则表达式虽然不易读懂,但有时它的确是最快的方法。re对象(标准库中的re模块)提供的强大sub方法,非常利于进行高效的正则表达式匹配替换。下面给出一个函数,该函数返回一个输入字符串的拷贝,该拷贝中的所有能够在指定字典中找到的子串都被替换为字典中的对应值:

import re
def multiple_replace(text, adict):
     rx = re.compile('|'.join(map(re.escape, adict)))
     def one_xlat(match):
           return adict[match.group(0)]
     return rx.sub(one_xlat, text)

讨论

本节展示了怎样使用Python的标准模块re来一次完成多个子串的替换。假设你有个基于字典的字符串的映射关系。字典的key就是你想要替换的子串,而字典中key的对应值则正是被用来做替代物的字符串。也可以针对字典的键值对应关系,调用字符串方法replace来完成替换,它将多次处理和创建原文本的复制,但逻辑却很清晰,速度也不错。不过re.sub的回调函数机制可以让处理方式变得更加简单。

首先,我们根据想要匹配的key创建一个正则表达式。这个正则表达式形式为a1|a2|...|aN,由N个需要被替换的字符串组成,并被竖线隔开,创建的方法也很简单,如代码所示,一行代码完成。然后,我们不直接给re.sub传递用于替换的字符串,而是传入一个回调函数参数。这样,每当遇到一次匹配,re.sub就会调用该回调函数,并将re.MatchObject的实例作为唯一参数传递给该回调函数,并期望着该回调函数返回作为替换物的字符串。在本例中,回调函数在字典中查找匹配的文本,并返回了对应值。

本节展示的函数multiple_replace,每次被调用时都会重新计算正则表达式并重新定义one_xlat辅助函数。但你经常只需要使用同一个固定不变的翻译表来完成很多文本的替换,这种情况下也许会希望只做一次准备工作。出于这种需求,也许会使用下面的基于闭包的方式:

import re
def make_xlat(*args, **kwds):
     adict = dict(*args, **kwds)
     rx = re.compile('|'.join(map(re.escape, adict)))
     def one_xlat(match):
           return adict[match.group(0)]
     def xlat(text):
           return rx.sub(one_xlat, text)
     return xlat

可以给make_xlat函数传递一个字典参数,或者其他的可以传递给内建的dict用于创建一个字典的参数组合;make_xlat返回一个xlat闭包,它只需要一个字符串参数text,并返回text的一个拷贝,该拷贝是根据字典给出的翻译表完成了替换之后的结果。

下面给出应用此函数的例子。通常我们可以把这个片段中的示例代码作为本节给出的代码源文件的一部分,这段代码受到前面的Python语句的保护不会被执行,除非这个模块被作为主脚本被执行:

if _ _name_ _ == "_ _main_ _":
       text = "Larry Wall is the creator of Perl"
       adict = {
          "Larry Wall" : "Guido van Rossum",
          "creator" : "Benevolent Dictator for Life",
          "Perl" : "Python",
       }
       print multiple_replace(text, adict)
       translate = make_xlat(adict)
       print translate(text)

本节中的替换任务常常是基于单词的替换任务,而不是基于任意一个子字符串。通过特殊的r’b’序列,正则表达式可以很好地找出单词的开始和结束位置。我们可以修改multiple_replace和make_xlat中创建和分配正则表达式rx的部分,从而完成一些自定义任务:

rx = re.compile(r'\b%s\b' % r'\b|\b'.join(map(re.escape, adict)))

其余的代码和本节前面给出的一样。但是,这种代码相似性可不是好事:那意味着我们需要很多相似的版本,每个创建正则表达式的部分都有点不同,我们可能会需要做大量的复制粘贴工作,这是代码复用中最糟糕的情况,另外在未来的维护上也增加了麻烦。

编写好代码的一个关键规则是:“一次,只做一次!”当我们注意到代码重复的时候,应该能够很快嗅到“不妙”的气味,并对原来的代码进行重构以提高复用性。因此,为了便于定制,我们更需要的是一个类,而不是函数或者闭包。下面给出个例子,我们实现了一个类,功能近似于make_xlat,但却能够通过子类化和重载进行定制:

class make_xlat:
      def _ _init_ _ (self, *args, **kwds):
            self.adict = dict(*args, **kwds)
            self.rx = self.make_rx( )
      def make_rx(self):
            return re.compile('|'.join(map(re.escape, self.adict)))
      def one_xlat(self, match):
            return self.adict[match.group(0)]
      def _ _call_ _ (self, text):
            return self.rx.sub(self.one_xlat, text)

这是对makexlt函数的一个完全的替代:另一方面,我们在这之前展示的代码,由于有if name == ‘ main _’来保护,即使make_xlat由以前的函数变成了类,也不会有什么问题。函数更加简单快速,但是类的优势是可以通过面向对象的方法—子类化或重载某些函数,轻易地实现重新定制。为了对单词进行翻译替换,代码可以这样写:

class make_xlat_by_whole_words(make_xlat):
      def make_rx(self):
           return re.compile(r'\b%s\b' % r'\b|\b'.join(map(re.escape, self.adict)))

通过简单的子类化和重载来实现定制化,我们避免了对代码的大量的复制和粘贴,这也是有时我们宁可舍弃更加简单的函数或者闭包不用,而使用面向对象结构的原因。仅仅把相关的功能打包成一个类并不能自动实现需要的定制。要实现高度的可定制性,在把功能划分成独立的类方法时必须具有一定的前瞻性。幸好,你不用逼自己第一次就把代码写得很完美;如果代码中没有能够符合任务需求的内部结构时(在这个例子中,我们通过子类化和选择性重载来复用代码),可以而且也应该对代码进行重构,构建出符合需求的结构。当然需要进行一些合适的测试来确保没有把原有的逻辑破坏掉,与此同时,你也完成了对自己的思想内容的重构。访问http://www.refactoring.com 可以获得更多关于重构的艺术和实践的信息。

相关文章
|
4月前
|
Python
Pycharm 如何更改成中文版| Python循环语句| for 和 else 的搭配使用
Pycharm 如何更改成中文版| Python循环语句| for 和 else 的搭配使用
22 0
|
Python
好教程推荐系列:力扣《Python Cookbook 3rd Edition》和《LeetCode Cookbook》
好教程推荐系列:力扣《Python Cookbook 3rd Edition》和《LeetCode Cookbook》
669 0
[雪峰磁针石博客]python代码风格指南(PEP8中文版)
本文给出主Python版本标准库的编码约定。CPython的C代码风格参见PEP7。 本文和PEP 257 文档字符串标准改编自Guido最初的《Python Style Guide》, 并增加了Barry的GNU Mailman Coding Style Guide的部分内容。
|
搜索推荐 Python
Khan Academy 的 Python 编程教学视频(中文版)
可汗学院(Khan Academy)是由孟加拉裔美国人,麻省理工学院及哈佛大学商学院毕业生萨尔曼·可汗在2006年创立的非营利教育组织。其目标是通过给所有人提供免费的世界级教育平台,来改善教育。
314 0
|
Web App开发 机器学习/深度学习 人工智能
Python生物学Cookbook - Bioinformatics with Python Cookbook 2nd -2018.pdf
在进入前20名大约3年后,统计语言R本月退出。这是非常令人惊讶的,因为统计编程领域仍然蓬勃发展,特别是由于数据挖掘和人工智能的普及。似乎统计编程市场正在进行整合。 Python已成为大赢家。可能的原因是统计编程现在正在从大学发展到工业,而Python更容易被业界接受。
|
测试技术 Python
书籍:Python Testing Cookbook, 2nd Edition - 2018.pdf python测试cookbook
简介 借助此基于解决方案的指南,修复Python中的日常测试问题 主要特点 使用doctest和unittest等强大的工具来方便测试 将自动化测试应用于非面向测试的现有遗留系统 使用真实示例简化Python测试的实用指南 图书说明 自动化测试是提高效率,同时减少软件测试缺陷的最佳方法。
|
机器学习/深度学习 人工智能 算法
python人工智能机器学习工具书籍: scikit-learn Cookbook 2nd Edition
简介 本书包括对机器学习中常见问题和不常见问题的演练和解决方案,以及如何利用scikit-learn有效地执行各种机器学习任务。 第二版首先介绍评估数据统计属性的方法,并为机器学习建模生成合成数据。当您逐步完成这些章节时,您会遇到一些食谱,它们将教您实施数据预处理,线性回归,逻辑回归,KNN,NaïveBayes,分类,决策树,合奏等技术。
|
Web App开发 测试技术 API
Python 资源大全中文版
GitHub 上有一个 Awesome - XXX 系列的资源整理,资源非常丰富,涉及面非常广。awesome-python 是 vinta 发起维护的 Python 资源列表,内容包括:Web框架、网络爬虫、网络内容提取、模板引擎、数据库、数据可视化、图片处理、文本处理、自然语言处理、机器学习、日志、代码分析等。
2853 0