diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..28ec9fe --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +*.md linguist-language=Python diff --git a/Advanced-Features/context.md b/Advanced-Features/context.md index 0716bbc..6807d9c 100644 --- a/Advanced-Features/context.md +++ b/Advanced-Features/context.md @@ -49,7 +49,7 @@ Exiting context # 调用了 __exit__ 方法 - Point(3, 4) 生成了一个上下文管理器; - 调用上下文管理器的 `__enter__()` 方法,并将 `__enter__()` 方法的返回值赋给 as 字句中的变量 pt; - 执行**语句体**(指 with 语句包裹起来的代码块)内容,输出 distance; -- 不管执行过程中是否发生异常,都执行上下文管理器的 `__exit__()` 方法。`__exit__()` 方法负责执行『清理』工作,如释放资源,关闭文件等。如果执行过程没有出现异常,或者语句体中执行了语句 break/continue/return,则以 None 作为参数调用 `__exit__(None, None, None) `;如果执行过程中出现异常,则使用 sys.exc_info 得到的异常信息为参数调用 `__exit__(exc_type, exc_value, exc_traceback)`; +- 不管执行过程中是否发生异常,都执行上下文管理器的 `__exit__()` 方法。`__exit__()` 方法负责执行『清理』工作,如释放资源,关闭文件等。如果执行过程没有出现异常,或者语句体中执行了语句 break/continue/return,则以 None 作为参数调用 `__exit__(None, None, None) `;如果执行过程中出现异常,则使用 `sys.exc_info` 得到的异常信息为参数调用 `__exit__(exc_type, exc_value, exc_traceback)`; - 出现异常时,如果 `__exit__(type, value, traceback)` 返回 False 或 None,则会重新抛出异常,让 with 之外的语句逻辑来处理异常;如果返回 True,则忽略异常,不再对异常进行处理; 上面的 with 语句执行过程没有出现异常,我们再来看出现异常的情形: diff --git a/Advanced-Features/generator.md b/Advanced-Features/generator.md index 14d916e..9e330da 100644 --- a/Advanced-Features/generator.md +++ b/Advanced-Features/generator.md @@ -132,7 +132,7 @@ for piece in read_in_chunks(f): 我们除了能对生成器进行迭代使它返回值外,还能: - 使用 `send()` 方法给它发送消息; -- 使用 `thow()` 方法给它发送异常; +- 使用 `throw()` 方法给它发送异常; - 使用 `close()` 方法关闭生成器; ## send() 方法 diff --git a/Class/class_and_object.md b/Class/class_and_object.md index 416f2bb..1f3e6b9 100644 --- a/Class/class_and_object.md +++ b/Class/class_and_object.md @@ -30,7 +30,7 @@ class Animal(object): 然后,在创建实例的时候,传入参数: ```python ->>> animal = Aniaml('dog1') # 传入参数 'dog1' +>>> animal = Animal('dog1') # 传入参数 'dog1' >>> animal.name # 访问对象的 name 属性 'dog1' ``` diff --git a/Class/singleton.md b/Class/singleton.md new file mode 100644 index 0000000..b88cee6 --- /dev/null +++ b/Class/singleton.md @@ -0,0 +1,126 @@ +# 单例模式 + +**单例模式(Singleton Pattern)**是一种常用的软件设计模式,该模式的主要目的是确保**某一个类只有一个实例存在**。当你希望在整个系统中,某个类只能出现一个实例时,单例对象就能派上用场。 + +比如,某个服务器程序的配置信息存放在一个文件中,客户端通过一个 AppConfig 的类来读取配置文件的信息。如果在程序运行期间,有很多地方都需要使用配置文件的内容,也就是说,很多地方都需要创建 AppConfig 对象的实例,这就导致系统中存在多个 AppConfig 的实例对象,而这样会严重浪费内存资源,尤其是在配置文件内容很多的情况下。事实上,类似 AppConfig 这样的类,我们希望在程序运行期间只存在一个实例对象。 + +在 Python 中,我们可以用多种方法来实现单例模式: + +- 使用模块 +- 使用 `__new__` +- 使用装饰器(decorator) +- 使用元类(metaclass) + +# 使用模块 + +其实,**Python 的模块就是天然的单例模式**,因为模块在第一次导入时,会生成 `.pyc` 文件,当第二次导入时,就会直接加载 `.pyc` 文件,而不会再次执行模块代码。因此,我们只需把相关的函数和数据定义在一个模块中,就可以获得一个单例对象了。如果我们真的想要一个单例类,可以考虑这样做: + +```python +# mysingleton.py +class My_Singleton(object): + def foo(self): + pass + +my_singleton = My_Singleton() +``` + +将上面的代码保存在文件 `mysingleton.py` 中,然后这样使用: + +``` +from mysingleton import my_singleton + +my_singleton.foo() +``` + +# 使用 `__new__` + +为了使类只能出现一个实例,我们可以使用 `__new__` 来控制实例的创建过程,代码如下: + +```python +class Singleton(object): + _instance = None + def __new__(cls, *args, **kw): + if not cls._instance: + cls._instance = super(Singleton, cls).__new__(cls, *args, **kw) + return cls._instance + +class MyClass(Singleton): + a = 1 +``` + +在上面的代码中,我们将类的实例和一个类变量 `_instance` 关联起来,如果 `cls._instance` 为 None 则创建实例,否则直接返回 `cls._instance`。 + +执行情况如下: + +``` +>>> one = MyClass() +>>> two = MyClass() +>>> one == two +True +>>> one is two +True +>>> id(one), id(two) +(4303862608, 4303862608) +``` + +# 使用装饰器 + +我们知道,装饰器(decorator)可以动态地修改一个类或函数的功能。这里,我们也可以使用装饰器来装饰某个类,使其只能生成一个实例,代码如下: + +``` +from functools import wraps + +def singleton(cls): + instances = {} + @wraps(cls) + def getinstance(*args, **kw): + if cls not in instances: + instances[cls] = cls(*args, **kw) + return instances[cls] + return getinstance + +@singleton +class MyClass(object): + a = 1 +``` + +在上面,我们定义了一个装饰器 `singleton`,它返回了一个内部函数 `getinstance`,该函数会判断某个类是否在字典 `instances` 中,如果不存在,则会将 `cls` 作为 key,`cls(*args, **kw)` 作为 value 存到 `instances` 中,否则,直接返回 `instances[cls]`。 + +# 使用 metaclass + +元类(metaclass)可以控制类的创建过程,它主要做三件事: + +- 拦截类的创建 +- 修改类的定义 +- 返回修改后的类 + +使用元类实现单例模式的代码如下: + +```python +class Singleton(type): + _instances = {} + def __call__(cls, *args, **kwargs): + if cls not in cls._instances: + cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs) + return cls._instances[cls] + +# Python2 +class MyClass(object): + __metaclass__ = Singleton + +# Python3 +# class MyClass(metaclass=Singleton): +# pass +``` + +# 小结 + +- Python 的模块是天然的单例模式,这在大部分情况下应该是够用的,当然,我们也可以使用装饰器、元类等方法 + +# 参考资料 + +- [Creating a singleton in Python - Stack Overflow](http://stackoverflow.com/questions/6760685/creating-a-singleton-in-python) +- [深入浅出单实例Singleton设计模式 | 酷 壳](http://coolshell.cn/articles/265.html) +- [design patterns - Python's use of __new__ and __init__? - Stack Overflow](http://stackoverflow.com/questions/674304/pythons-use-of-new-and-init) + + diff --git a/Datatypes/README.md b/Datatypes/README.md index 44fd8c2..397145e 100644 --- a/Datatypes/README.md +++ b/Datatypes/README.md @@ -7,7 +7,7 @@ 所有序列类型都可以进行某些通用的操作,比如: - 索引(indexing) -- 分片(sliceing) +- 分片(slicing) - 迭代(iteration) - 加(adding) - 乘(multiplying) diff --git a/Datatypes/set.md b/Datatypes/set.md index 32d4fd6..4feb79a 100644 --- a/Datatypes/set.md +++ b/Datatypes/set.md @@ -56,7 +56,7 @@ set(['a', 'c', 'b', 4, 'd', 'e']) ## 删除元素 -`remove()` 方法可以删除集合中的元素。 +`remove()` 方法可以删除集合中的元素, 但是删除不存在的元素,会抛出 KeyError,可改用 `discard()`。 看看例子: @@ -67,10 +67,11 @@ set(['a', 'c', 'b', 'd']) >>> s.remove('a') # 删除元素 'a' >>> s set(['c', 'b', 'd']) ->>> s.remove('e') # 删除不存在的元素,会抛出 KeyErro +>>> s.remove('e') # 删除不存在的元素,会抛出 KeyError Traceback (most recent call last): File "", line 1, in KeyError: 'e' +>>> s.discard('e') # 删除不存在的元素, 不会抛出 KeyError ``` ## 交集/并集/差集 diff --git a/File-Directory/os.md b/File-Directory/os.md index aa1b7b6..75cac12 100644 --- a/File-Directory/os.md +++ b/File-Directory/os.md @@ -109,7 +109,7 @@ False - os.walk -os.walk 是遍历目录常用的模块,它返回一个包含 3 个元素的元祖:(dirpath, dirnames, filenames)。dirpath 是以 string 字符串形式返回该目录下所有的绝对路径;dirnames 是以列表 list 形式返回每一个绝对路径下的文件夹名字;filesnames 是以列表 list 形式返回该路径下所有文件名字。 +os.walk 是遍历目录常用的模块,它返回一个包含 3 个元素的元组:(dirpath, dirnames, filenames)。dirpath 是以 string 字符串形式返回该目录下所有的绝对路径;dirnames 是以列表 list 形式返回每一个绝对路径下的文件夹名字;filenames 是以列表 list 形式返回该路径下所有文件名字。 ```python >>> for root, dirs, files in os.walk('/Users/ethan/coding'): diff --git a/Functional/decorator.md b/Functional/decorator.md index ad388e8..d6c4309 100644 --- a/Functional/decorator.md +++ b/Functional/decorator.md @@ -313,7 +313,7 @@ def hello(): 'wrapped' ``` -为了消除这样的副作用,Python 中的 functool 包提供了一个 wraps 的装饰器: +为了消除这样的副作用,Python 中的 functools 包提供了一个 wraps 的装饰器: ```python from functools import wraps diff --git a/HTTP/Requests.md b/HTTP/Requests.md index c8b3dde..be2efc2 100644 --- a/HTTP/Requests.md +++ b/HTTP/Requests.md @@ -331,30 +331,28 @@ r.raw.read(10) >>> import requests >>> headers = {'User-Agent': 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)'} ->>> r = requests.get('https://toutiao.io/shares/789751/url', headers=headers) +>>> r = requests.get('https://toutiao.io/k/c32y51', headers=headers) >>> r.status_code 200 >>> r.url # 发生了重定向,响应对象的 url,跟请求对象不一样 -u'https://funhacks.net/2016/12/06/flask_react_news/?hmsr=toutiao.io&utm_medium=toutiao.io&utm_source=toutiao.io' +u'http://www.jianshu.com/p/490441391db6?hmsr=toutiao.io&utm_medium=toutiao.io&utm_source=toutiao.io' >>> r.history -[, ] ->>> r.history[0].text -u'You are being redirected.' +[] ->>> r.history[1].text -u'You are being redirected.' +>>> r.history[0].text +u'You are being redirected.' ``` -可以看到,我们访问网址 `https://toutiao.io/shares/789751/url` 被重定向到了下面的链接: +可以看到,我们访问网址 `https://toutiao.io/k/c32y51` 被重定向到了下面的链接: ```python -u'https://funhacks.net/2016/12/06/flask_react_news/?hmsr=toutiao.io&utm_medium=toutiao.io&utm_source=toutiao.io' +http://www.jianshu.com/p/490441391db6?hmsr=toutiao.io&utm_medium=toutiao.io&utm_source=toutiao.io' ``` -我们还看到 `r.history` 包含了两个 Response 对象列表,我们可以用它来追踪重定向。 +我们还看到 `r.history` 包含了一个 Response 对象列表,我们可以用它来追踪重定向。 如果请求方法是 GET、POST、PUT、OPTIONS、PATCH 或 DELETE,我们可以通过 `all_redirects` 参数禁止重定向: @@ -362,15 +360,13 @@ u'https://funhacks.net/2016/12/06/flask_react_news/?hmsr=toutiao.io&utm_medium=t >>> import requests >>> headers = {'User-Agent': 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)'} ->>> r = requests.get('https://toutiao.io/shares/789751/url', headers=headers, allow_redirects=False) ->>> r.status_code -200 +>>> r = requests.get('https://toutiao.io/k/c32y51', headers=headers, allow_redirects=False) >>> r.url # 禁止重定向,响应对象的 url 跟请求对象一致 -u'https://toutiao.io/shares/789751/url' +u'https://toutiao.io/k/c32y51' >>> r.history [] >>> r.text -u'You are being redirected.' +u'You are being redirected.' ``` # Cookie diff --git a/PYTHONroadmap.png b/PYTHONroadmap.png new file mode 100644 index 0000000..8a2e097 Binary files /dev/null and b/PYTHONroadmap.png differ diff --git a/Process-Thread-Coroutine/process.md b/Process-Thread-Coroutine/process.md index a75f91c..8861568 100644 --- a/Process-Thread-Coroutine/process.md +++ b/Process-Thread-Coroutine/process.md @@ -129,6 +129,29 @@ pid:11747, num is 13 pid:11748, num is 13 ``` +这是因为根据不同的平台,multiprocessing支持三种启动进程的办法: +#### spawn +父进程会启动一个新的解释器,子进程只会继承run()所需的资源。 +不必要的文件描述符和句柄(一种指针)不会被继承。 +该方法和fork,forkserver相比,启动进程较慢。 + +可在Unix和Windows上使用。 Windows上的默认设置。 + +#### fork +父进程使用 os.fork() 来产生 Python 解释器分叉。 +子进程在开始时实际上与父进程相同,并且会继承父进程的所有资源。 +多线程的安全是有问题的。 + +只能在Unix上使用。Unix上的默认设置。 + +#### forkserver +程序启动并选择forkserver启动方法时,将启动一个服务器进程。 +之后每当需要一个新进程时,父进程就会连接到服务器并请求它分叉一个新进程。 +分叉服务器进程是单线程的,因此使用 os.fork() 是安全的。 +没有不必要的资源被继承。 + +可在Unix平台上使用,并支持通过Unix管道传递文件描述符。 + ## 使用进程池创建多个进程 在上面,我们只是创建了一个进程,如果要创建多个进程呢?Python 提供了**进程池**的方式,让我们批量创建子进程,让我们看一个简单的示例: @@ -254,9 +277,7 @@ DONE - [编程中的进程、线程、协程、同步、异步、回调 · 浮生半日闲](https://wangdashuaihenshuai.github.io/2015/10/17/%E7%BC%96%E7%A8%8B%E4%B8%AD%E7%9A%84%E8%BF%9B%E7%A8%8B%E3%80%81%E7%BA%BF%E7%A8%8B%E3%80%81%E5%8D%8F%E7%A8%8B%E3%80%81%E5%90%8C%E6%AD%A5%E3%80%81%E5%BC%82%E6%AD%A5%E3%80%81%E5%9B%9E%E8%B0%83/) - [python中多进程以及多线程编程的总结 - Codefly](http://sunms.codefly.top/2016/11/05/python%E4%B8%AD%E5%A4%9A%E8%BF%9B%E7%A8%8B%E4%BB%A5%E5%8F%8A%E5%A4%9A%E7%BA%BF%E7%A8%8B%E7%BC%96%E7%A8%8B%E7%9A%84%E6%80%BB%E7%BB%93/) - [multithreading - Python multiprocessing.Pool: when to use apply, apply_async or map? - Stack Overflow](http://stackoverflow.com/questions/8533318/python-multiprocessing-pool-when-to-use-apply-apply-async-or-map) - +- [multiprocessing --- 基于进程的并行¶](https://docs.python.org/zh-cn/3/library/multiprocessing.html) [mp]: https://docs.python.org/2/library/multiprocessing.html - - diff --git a/README.md b/README.md index e1a6966..e33cca7 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,20 @@ -![cover](https://ofaatpail.qnssl.com/2017-01-03-explore-python-cover.png) +![cover](https://i.loli.net/2020/03/23/9B1oWLeu6Q3Hbrz.png) Python 之旅 === +![Version](https://img.shields.io/badge/version-1.0-brightgreen.svg) +[![License: CC BY-NC-ND 4.0](https://img.shields.io/badge/License-CC%20BY--NC--ND%204.0-brightgreen.svg)](https://raw.githubusercontent.com/ethan-funny/explore-python/master/LICENSE) + ## Python 简介 Python 诞生于 1989 年的圣诞期间,由 [Guido van Rossum](https://gvanrossum.github.io/) 开发而成,目前 Guido 仍然是 Python 的主要开发者,主导着 Python 的发展方向,Python 社区经常称呼他为『仁慈的独裁者』。 Python 是一门面向对象,解释型的高级程序设计语言,它的语法非常简洁、优雅,而这也是 Python 的一些设计哲学。Python 自带了很完善的库,涵盖了数据库,网络,文件处理,GUI 等方方面面,通过这些库,我们可以比较快速地解决一些棘手问题,也可以将其作为基础库,开发出一些高级库。 -目前 Python 在大部分领域都占有一席之地,比如 Web 开发,机器学习,科学计算等。不少大型网站都是使用 Python 作为后台开发语言的,比如 [YouTube](https://www.youtube.com/)、[Pinterest](https://www.pinterest.com/)、国内的[豆瓣](https://www.douban.com/)和[知乎](http://www.zhihu.com/)等。另外,有不少知名的机器学习库也是使用 Python 开发的,比如,[scikit-learn](http://scikit-learn.org/stable/) 是一个强大的机器学习库,[Theano](https://github.com/Theano/Theano) 是一个成熟的深度学习库。 +目前 Python 在大部分领域都占有一席之地,比如 Web 开发,机器学习,科学计算等。不少大型网站都是使用 Python 作为后台开发语言的,比如 [YouTube](https://www.youtube.com/)、[Pinterest](https://www.pinterest.com/)、国内的[豆瓣](https://www.douban.com/)和[知乎](http://www.zhihu.com/)等。 + +另外,有不少知名的机器学习库也是使用 Python 开发的,比如,[scikit-learn](http://scikit-learn.org/stable/) 是一个强大的机器学习库,[PyTorch](https://pytorch.org/) 是一个成熟的深度学习库。 当然了,Python 也有一些缺点。Python 经常被人们吐槽的一点就是:运行速度慢,和 C/C++ 相比非常慢。但是,除了像视频高清解码等**计算密集型任务**对运行速度有较高的要求外,在大部分时候,我们可能并不需要非常快的运行速度。比如,一个程序使用 C 来实现,运行时间只需 0.01 秒,而使用 Python 来实现,需要 0.1 秒,虽然 Python 的运行时间是 C 的 10 倍,显然很慢,但对我们而言,这压根不是问题。 @@ -21,7 +26,8 @@ Python 是一门面向对象,解释型的高级程序设计语言,它的语 首先,我参考一些相关的书籍,作了一个基础的思维导图,如下: -![思维导图](https://ofaatpail.qnssl.com/2017-01-03-explore-python2.png) +![思维导图](https://i.loli.net/2020/03/23/uZN8aehmwl14XcG.png) +![Eng journy](PYTHONroadmap.png) 接下来,就要开始写作了,这也是最艰难的一关。 @@ -54,11 +60,6 @@ Python 是一门面向对象,解释型的高级程序设计语言,它的语 本书将会持续进行修订和更新,读者如果遇到问题,请及时向我反馈,我会在第一时间加以解决。 -## 下载电子版 - -目前本书暂时提供 epub 格式的电子版,因为生成的 pdf 和 mobi 版本并不是很美观,不利于阅读,故暂不提供。 - -[点击下载 epub 版本](https://github.com/ethan-funny/explore-python/files/691859/explore-python.epub.zip) ## 声明 @@ -76,19 +77,13 @@ Python 是一门面向对象,解释型的高级程序设计语言,它的语 | 时间 | 说明 | | :---: | :---: | | 2017-01-03 | 发布版本 v1.0 | +| 2019-02-09 | fix typo | -## 联系我 - -如果你对于本书有什么建议或意见,欢迎批评指正,并联系我。 - -- [个人主页](https://funhacks.net) -- [GitHub](https://github.com/ethan-funny) -- [Twitter](https://twitter.com/pihacks) ## 支持我 如果你觉得本书对你有所帮助,不妨请我喝杯咖啡,感谢支持! - + diff --git a/SUMMARY.md b/SUMMARY.md index 1b73d93..dc8cb78 100644 --- a/SUMMARY.md +++ b/SUMMARY.md @@ -53,6 +53,7 @@ * [argparse](Standard-Modules/argparse.md) * [base64](Standard-Modules/base64.md) * [collections](Standard-Modules/collections.md) + * [itertools](Standard-Modules/itertools.md) * [datetime](Standard-Modules/datetime.md) * [hashlib](Standard-Modules/hashlib.md) * [hmac](Standard-Modules/hmac.md) diff --git a/Standard-Modules/hashlib.md b/Standard-Modules/hashlib.md index e98d2cc..7b841d4 100644 --- a/Standard-Modules/hashlib.md +++ b/Standard-Modules/hashlib.md @@ -47,7 +47,7 @@ SHA1 的使用和 MD5 的使用类似: # 小结 -- MD5 经常用来做用户密码的存储,而 SHA1 则经常用作数字签名。 +- MD5 以前经常用来做用户密码的存储。2004年,它被证明无法防止碰撞,因此无法适用于安全性认证,但仍广泛应用于普通数据的错误检查领域。如果你需要储存用户密码,你应该去了解一下诸如 [PBKDF2](https://en.wikipedia.org/wiki/PBKDF2) 或 [bcrypt](https://en.wikipedia.org/wiki/Bcrypt) 之类的算法。而 SHA1 则经常用作数字签名。 # 参考资料 diff --git a/Standard-Modules/itertools.md b/Standard-Modules/itertools.md new file mode 100644 index 0000000..a3eadf8 --- /dev/null +++ b/Standard-Modules/itertools.md @@ -0,0 +1,545 @@ +# itertools + +我们知道,迭代器的特点是:**惰性求值**(Lazy evaluation),即只有当迭代至某个值时,它才会被计算,这个特点使得迭代器特别适合于遍历大文件或无限集合等,因为我们不用一次性将它们存储在内存中。 + +Python 内置的 itertools 模块包含了一系列用来产生不同类型迭代器的函数或类,这些函数的返回都是一个迭代器,我们可以通过 for 循环来遍历取值,也可以使用 `next()` 来取值。 + +itertools 模块提供的迭代器函数有以下几种类型: + +- 无限迭代器:生成一个无限序列,比如自然数序列 `1, 2, 3, 4, ...`; +- 有限迭代器:接收一个或多个序列(sequence)作为参数,进行组合、分组和过滤等; +- 组合生成器:序列的排列、组合,求序列的笛卡儿积等; + +# 无限迭代器 + +itertools 模块提供了三个函数(事实上,它们是类)用于生成一个无限序列迭代器: + +- count(firstval=0, step=1) + + 创建一个从 firstval (默认值为 0) 开始,以 step (默认值为 1) 为步长的的无限整数迭代器 + +- cycle(iterable) + + 对 iterable 中的元素反复执行循环,返回迭代器 + +- repeat(object [,times] + + 反复生成 object,如果给定 times,则重复次数为 times,否则为无限 + +下面,让我们看看一些例子。 + +## count + +`count()` 接收两个参数,第一个参数指定开始值,默认为 0,第二个参数指定步长,默认为 1: + +```python +>>> import itertools +>>> +>>> nums = itertools.count() +>>> for i in nums: +... if i > 6: +... break +... print i +... +0 +1 +2 +3 +4 +5 +6 +>>> nums = itertools.count(10, 2) # 指定开始值和步长 +>>> for i in nums: +... if i > 20: +... break +... print i +... +10 +12 +14 +16 +18 +20 +``` + +## cycle + +`cycle()` 用于对 iterable 中的元素反复执行循环: + +```python +>>> import itertools +>>> +>>> cycle_strings = itertools.cycle('ABC') +>>> i = 1 +>>> for string in cycle_strings: +... if i == 10: +... break +... print i, string +... i += 1 +... +1 A +2 B +3 C +4 A +5 B +6 C +7 A +8 B +9 C +``` + +## repeat + +`repeat()` 用于反复生成一个 object: + +```python +>>> import itertools +>>> +>>> for item in itertools.repeat('hello world', 3): +... print item +... +hello world +hello world +hello world +>>> +>>> for item in itertools.repeat([1, 2, 3, 4], 3): +... print item +... +[1, 2, 3, 4] +[1, 2, 3, 4] +[1, 2, 3, 4] +``` + +# 有限迭代器 + +itertools 模块提供了多个函数(类),接收一个或多个迭代对象作为参数,对它们进行组合、分组和过滤等: + +- chain() +- compress() +- dropwhile() +- groupby() +- ifilter() +- ifilterfalse() +- islice() +- imap() +- starmap() +- tee() +- takewhile() +- izip() +- izip_longest() + +## chain + +`chain` 的使用形式如下: + +``` +chain(iterable1, iterable2, iterable3, ...) +``` + +`chain` 接收多个可迭代对象作为参数,将它们『连接』起来,作为一个新的迭代器返回。 + +```python +>>> from itertools import chain +>>> +>>> for item in chain([1, 2, 3], ['a', 'b', 'c']): +... print item +... +1 +2 +3 +a +b +c +``` + +`chain` 还有一个常见的用法: + +``` +chain.from_iterable(iterable) +``` + +接收一个可迭代对象作为参数,返回一个迭代器: + +```python +>>> from itertools import chain +>>> +>>> string = chain.from_iterable('ABCD') +>>> string.next() +'A' +``` + +## compress + +`compress` 的使用形式如下: + +``` +compress(data, selectors) +``` + +`compress` 可用于对数据进行筛选,当 selectors 的某个元素为 true 时,则保留 data 对应位置的元素,否则去除: + +```python +>>> from itertools import compress +>>> +>>> list(compress('ABCDEF', [1, 1, 0, 1, 0, 1])) +['A', 'B', 'D', 'F'] +>>> list(compress('ABCDEF', [1, 1, 0, 1])) +['A', 'B', 'D'] +>>> list(compress('ABCDEF', [True, False, True])) +['A', 'C'] +``` + +## dropwhile + +`dropwhile` 的使用形式如下: + +``` +dropwhile(predicate, iterable) +``` + +其中,predicate 是函数,iterable 是可迭代对象。对于 iterable 中的元素,如果 predicate(item) 为 true,则丢弃该元素,否则返回该项及所有后续项。 + +```python +>>> from itertools import dropwhile +>>> +>>> list(dropwhile(lambda x: x < 5, [1, 3, 6, 2, 1])) +[6, 2, 1] +>>> +>>> list(dropwhile(lambda x: x > 3, [2, 1, 6, 5, 4])) +[2, 1, 6, 5, 4] +``` + +## groupby + +`groupby` 用于对序列进行分组,它的使用形式如下: + +``` +groupby(iterable[, keyfunc]) +``` + +其中,iterable 是一个可迭代对象,keyfunc 是分组函数,用于对 iterable 的连续项进行分组,如果不指定,则默认对 iterable 中的连续相同项进行分组,返回一个 `(key, sub-iterator)` 的迭代器。 + +```python +>>> from itertools import groupby +>>> +>>> for key, value_iter in groupby('aaabbbaaccd'): +... print key, ':', list(value_iter) +... +a : ['a', 'a', 'a'] +b : ['b', 'b', 'b'] +a : ['a', 'a'] +c : ['c', 'c'] +d : ['d'] +>>> +>>> data = ['a', 'bb', 'ccc', 'dd', 'eee', 'f'] +>>> for key, value_iter in groupby(data, len): # 使用 len 函数作为分组函数 +... print key, ':', list(value_iter) +... +1 : ['a'] +2 : ['bb'] +3 : ['ccc'] +2 : ['dd'] +3 : ['eee'] +1 : ['f'] +>>> +>>> data = ['a', 'bb', 'cc', 'ddd', 'eee', 'f'] +>>> for key, value_iter in groupby(data, len): +... print key, ':', list(value_iter) +... +1 : ['a'] +2 : ['bb', 'cc'] +3 : ['ddd', 'eee'] +1 : ['f'] +``` + +## ifilter + +`ifilter` 的使用形式如下: + +``` +ifilter(function or None, sequence) +``` + +将 iterable 中 function(item) 为 True 的元素组成一个迭代器返回,如果 function 是 None,则返回 iterable 中所有计算为 True 的项。 + +```python +>>> from itertools import ifilter +>>> +>>> list(ifilter(lambda x: x < 6, range(10))) +[0, 1, 2, 3, 4, 5] +>>> +>>> list(ifilter(None, [0, 1, 2, 0, 3, 4])) +[1, 2, 3, 4] +``` + +## ifilterfalse + +`ifilterfalse` 的使用形式和 `ifilter` 类似,它将 iterable 中 function(item) 为 False 的元素组成一个迭代器返回,如果 function 是 None,则返回 iterable 中所有计算为 False 的项。 + +```python +>>> from itertools import ifilterfalse +>>> +>>> list(ifilterfalse(lambda x: x < 6, range(10))) +[6, 7, 8, 9] +>>> +>>> list(ifilter(None, [0, 1, 2, 0, 3, 4])) +[0, 0] +``` + +## islice + +`islice` 是切片选择,它的使用形式如下: + +``` +islice(iterable, [start,] stop [, step]) +``` + +其中,iterable 是可迭代对象,start 是开始索引,stop 是结束索引,step 是步长,start 和 step 可选。 + +```python +>>> from itertools import count, islice +>>> +>>> list(islice([10, 6, 2, 8, 1, 3, 9], 5)) +[10, 6, 2, 8, 1] +>>> +>>> list(islice(count(), 6)) +[0, 1, 2, 3, 4, 5] +>>> +>>> list(islice(count(), 3, 10)) +[3, 4, 5, 6, 7, 8, 9] +>>> list(islice(count(), 3, 10 ,2)) +[3, 5, 7, 9] +``` + +## imap + +`imap` 类似 `map` 操作,它的使用形式如下: + +``` +imap(func, iter1, iter2, iter3, ...) +``` + +`imap` 返回一个迭代器,元素为 `func(i1, i2, i3, ...)`,`i1`,`i2` 等分别来源于 `iter`, `iter2`。 + +```python +>>> from itertools import imap +>>> +>>> imap(str, [1, 2, 3, 4]) + +>>> +>>> list(imap(str, [1, 2, 3, 4])) +['1', '2', '3', '4'] +>>> +>>> list(imap(pow, [2, 3, 10], [4, 2, 3])) +[16, 9, 1000] +``` + +## tee + +`tee` 的使用形式如下: + +``` +tee(iterable [,n]) +``` + +`tee` 用于从 iterable 创建 n 个独立的迭代器,以元组的形式返回,n 的默认值是 2。 + +```python +>>> from itertools import tee +>>> +>>> tee('abcd') # n 默认为 2,创建两个独立的迭代器 +(, ) +>>> +>>> iter1, iter2 = tee('abcde') +>>> list(iter1) +['a', 'b', 'c', 'd', 'e'] +>>> list(iter2) +['a', 'b', 'c', 'd', 'e'] +>>> +>>> tee('abc', 3) # 创建三个独立的迭代器 +(, , ) +``` + +## takewhile + +`takewhile` 的使用形式如下: + +``` +takewhile(predicate, iterable) +``` + +其中,predicate 是函数,iterable 是可迭代对象。对于 iterable 中的元素,如果 predicate(item) 为 true,则保留该元素,只要 predicate(item) 为 false,则立即停止迭代。 + +```python +>>> from itertools import takewhile +>>> +>>> list(takewhile(lambda x: x < 5, [1, 3, 6, 2, 1])) +[1, 3] +>>> list(takewhile(lambda x: x > 3, [2, 1, 6, 5, 4])) +[] +``` + +## izip + +`izip` 用于将多个可迭代对象**对应位置**的元素作为一个元组,将所有元组『组成』一个迭代器,并返回。它的使用形式如下: + +``` +izip(iter1, iter2, ..., iterN) +``` + +如果某个可迭代对象不再生成值,则迭代停止。 + +```python +>>> from itertools import izip +>>> +>>> for item in izip('ABCD', 'xy'): +... print item +... +('A', 'x') +('B', 'y') +>>> for item in izip([1, 2, 3], ['a', 'b', 'c', 'd', 'e']): +... print item +... +(1, 'a') +(2, 'b') +(3, 'c') +``` + +## izip_longest + +`izip_longest` 跟 `izip` 类似,但迭代过程会持续到所有可迭代对象的元素都被迭代完。它的形式如下: + +``` +izip_longest(iter1, iter2, ..., iterN, [fillvalue=None]) +``` + +如果有指定 fillvalue,则会用其填充缺失的值,否则为 None。 + +```python +>>> from itertools import izip_longest +>>> +>>> for item in izip_longest('ABCD', 'xy'): +... print item +... +('A', 'x') +('B', 'y') +('C', None) +('D', None) +>>> +>>> for item in izip_longest('ABCD', 'xy', fillvalue='-'): +... print item +... +('A', 'x') +('B', 'y') +('C', '-') +('D', '-') +``` + +# 组合生成器 + +itertools 模块还提供了多个组合生成器函数,用于求序列的排列、组合等: + +- product +- permutations +- combinations +- combinations_with_replacement + +## product + +`product` 用于求多个可迭代对象的笛卡尔积,它跟嵌套的 for 循环等价。它的一般使用形式如下: + +``` +product(iter1, iter2, ... iterN, [repeat=1]) +``` + +其中,repeat 是一个关键字参数,用于指定重复生成序列的次数, + + +```python +>>> from itertools import product +>>> +>>> for item in product('ABCD', 'xy'): +... print item +... +('A', 'x') +('A', 'y') +('B', 'x') +('B', 'y') +('C', 'x') +('C', 'y') +('D', 'x') +('D', 'y') +>>> +>>> list(product('ab', range(3))) +[('a', 0), ('a', 1), ('a', 2), ('b', 0), ('b', 1), ('b', 2)] +>>> +>>> list(product((0,1), (0,1), (0,1))) +[(0, 0, 0), (0, 0, 1), (0, 1, 0), (0, 1, 1), (1, 0, 0), (1, 0, 1), (1, 1, 0), (1, 1, 1)] +>>> +>>> list(product('ABC', repeat=2)) +[('A', 'A'), ('A', 'B'), ('A', 'C'), ('B', 'A'), ('B', 'B'), ('B', 'C'), ('C', 'A'), ('C', 'B'), ('C', 'C')] +>>> +``` + +## permutations + +`permutations` 用于生成一个排列,它的一般使用形式如下: + +``` +permutations(iterable[, r]) +``` + +其中,r 指定生成排列的元素的长度,如果不指定,则默认为可迭代对象的元素长度。 + +```python +>>> from itertools import permutations +>>> +>>> permutations('ABC', 2) + +>>> +>>> list(permutations('ABC', 2)) +[('A', 'B'), ('A', 'C'), ('B', 'A'), ('B', 'C'), ('C', 'A'), ('C', 'B')] +>>> +>>> list(permutations('ABC')) +[('A', 'B', 'C'), ('A', 'C', 'B'), ('B', 'A', 'C'), ('B', 'C', 'A'), ('C', 'A', 'B'), ('C', 'B', 'A')] +>>> +``` + +## combinations + +`combinations` 用于求序列的组合,它的使用形式如下: + +``` +combinations(iterable, r) +``` + +其中,r 指定生成组合的元素的长度。 + +```python +>>> from itertools import combinations +>>> +>>> list(combinations('ABC', 2)) +[('A', 'B'), ('A', 'C'), ('B', 'C')] +``` + +## combinations_with_replacement + +`combinations_with_replacement` 和 `combinations` 类似,但它生成的组合包含自身元素。 + +```python +>>> from itertools import combinations_with_replacement +>>> +>>> list(combinations_with_replacement('ABC', 2)) +[('A', 'A'), ('A', 'B'), ('A', 'C'), ('B', 'B'), ('B', 'C'), ('C', 'C')] +``` + +# 小结 + +- itertools 模块提供了很多用于产生多种类型迭代器的函数,它们的返回值不是 list,而是迭代器。 + +# 参考链接 + +- [itertools — Functions creating iterators for efficient looping](https://docs.python.org/2/library/itertools.html) +- [itertools – Iterator functions for efficient looping - Python Module of the Week](https://pymotw.com/2/itertools/) + + diff --git a/book.json b/book.json index a879492..0520c60 100644 --- a/book.json +++ b/book.json @@ -12,7 +12,6 @@ "weibo": null }, "sidebar": { - "个人主页": "https://funhacks.net", "书籍源码": "https://github.com/ethan-funny/explore-python/" } },