30-源码解密内置函数 iter、next
30-源码解密内置函数 iter、next楔子这次我们来看看 iter 和 next 这两个内置函数的用法,我们知道 iter 是将一个可迭代对象变成一个迭代器,next 是将迭代器里的值一步一步迭代出来。
123456lst = [1, 2, 3]it = iter(lst)print(it) # <list_iterator object at 0x000001DC6E898640># 调用next, 可以对迭代器进行迭代print(next(it)) # 1
注意:iter 还有一个鲜为人知的用法,我们来看一下:
123456789101112131415161718192021val = 0def foo(): global val val += 1 return val# iter可以接收一个参数: iter(可迭代对象)# iter可以接收两个参数: iter(可调用对象, value)for i in iter(foo, 5): print(i)"""1234"""# 进 ...
29-源码解密 map、filter、zip 底层实现,对比列表解析式
29-源码解密 map、filter、zip 底层实现,对比列表解析式楔子Python 现在如此流行,拥有众多开源、高质量的第三方库是一个重要原因,不过 Python 的简单、灵巧、容易上手也是功不可没的,而其背后的内置函数(类)则起到了很大的作用。举个栗子:
123456789101112numbers = [1, 2, 3, 4, 5]# 将里面每一个元素都加1print(list(map(lambda x: x + 1, numbers))) # [2, 3, 4, 5, 6]strings = ["abc", "d", "def", "kf", "ghtc"]# 筛选出长度大于等于3的print(list(filter(lambda x: len(x) >= 3, strings))) # ['abc', 'def', 'ghtc']keys = ["name", "age&quo ...
28-Python内存管理与垃圾回收(第二部分):源码解密Python中的垃圾回收机制
28-Python内存管理与垃圾回收(第二部分):源码解密Python中的垃圾回收机制楔子现在绝大部分的语言都实现了垃圾回收机制,这其中也包括Python,而不同的语言采用的垃圾回收算法也各不相同。那么,常见的垃圾回收算法都有哪些呢?
引用计数法(reference count): 记录对象的被引用次数, 引用计数降为0时回收
标记-清除法(mark-sweep): 从根集合触发, 遍历所有能访问到的对象并对其进行标记, 然后将未被标记的对象清除
停止-复制法(stop-copy): 将内存划分为大小相同的内存块, 一块用完后启用另一块、并将存活的对象拷贝过去, 原来那块则整体被回收
分代回收法(generational-collection): 根据对象的存活时间将对象分为若干代, 并按照不同代的特征采用最合适的回收策略
那么我们下面来看看Python中的垃圾回收。
引用计数对于Python而言,其对象的生命周期是通过对象的引用计数来管理的,这一点在开始的章节我们就说了,对于Python中实现对象的基石PyObject,有两个属性,一个是该对象的类型,还有一个就是引用计数(ob_ ...
27-Python内存管理与垃圾回收(第一部分):深度剖析Python内存管理架构、内存池的实现原理
27-Python内存管理与垃圾回收(第一部分):深度剖析Python内存管理架构、内存池的实现原理楔子内存管理,对于Python这样的动态语言来说是非常重要的一部分,它在很大程度上决定了Python的执行效率,因为Python在运行中会创建和销毁大量的对象,这些都涉及内存的管理,因此精湛的内存管理技术是确保内存使用效率的关键。
此外,我们知道Python还是一门提供了垃圾回收机制(GC, garbage collection)的语言,可以将开发者从繁琐的手动维护内存的工作中解放出来。
那么下面我们就来分析一下Python中的内存管理和垃圾回收。
内存管理架构首先Python的内存管理机制是分层次的,我们可以看成是有6层:-2、-1、0、1、2、3。
最底层,也就是-2和-1层是由操作系统提供的内存管理接口,因为计算机硬件资源由操作系统负责管理,内存资源也不例外,应用程序通过系统调用向操作系统申请内存。注意:这一层Python是无权干预的。
第0层,C的库函数会将系统调用封装成通用的内存分配器,也就是我们所熟悉的malloc系列函数。注意:这一层Python同样无法干预。
第1、2、 ...
26-解密Python中的多线程(第二部分):源码剖析Python线程的创建、销毁、调度、以及GIL的实现原理
26-解密Python中的多线程(第二部分):源码剖析Python线程的创建、销毁、调度、以及GIL的实现原理初见Python的_thread模块下面我们来说一下Python中线程的创建,我们知道在创建多线程的时候会使用threading这个标准库,这个库是以一个py文件存在的形式存在的,不过这个模块依赖于_thread模块,我们来看看它长什么样子。
_thread是真正用来创建线程的模块,这个模块是由C编写,内嵌在解释器里面。我们可以import调用,但是在Python安装目录里面则是看不到的。像这种底层由C编写、内嵌在解释器里面的模块,以及那些无法使用文本打开的pyd文件,pycharm都会给你做一个抽象,并且把注释给你写好。
记得我们之前说过Python源码中的Modules目录,这个目录里面存放了大量使用C编写的模块,我们在编译完Python之后就,这些模块就内嵌在解释器里面了。而这些模块都是针对那些性能要求比较高的,而要求不高的则由Python编写,存放在Lib目录下。像我们平时调用random、collections、threading,其实它们背后会调用_random、 ...
25-解密Python中的多线程(第一部分):初识GIL、以及多个线程之间的调度机制
25-解密Python中的多线程(第一部分):初识GIL、以及多个线程之间的调度机制楔子这次我们来说一下Python中的多线程,在上篇博客中我们说了Python的线程,我们说Python中的线程是对OS线程进行了一个封装,并提供了一个线程状态(PyThreadState)对象,来记录OS线程的一些状态信息。
那什么是多线程呢?首先线程是操作系统调度cpu工作的最小单元,同理进程则是操作系统资源分配的最小单元,线程是需要依赖于进程的,并且每一个进程只少有一个线程,这个线程我们称之为主线程。而主线程则可以创建子线程,一个进程中如果有多个线程去工作,我们就称之为多线程。
开发一个多线程应用程序是很常见的事情,很多语言都支持多线程,有的是原生支持,有的是通过库的支持。而Python毫无疑问也支持多线程,并且它是通过threading这个库的方式实现的。另外提到Python的多线程,会让人想到GIL(global interpreter lock)这个万恶之源,我们后面会详细介绍。目前我们知道Python中的多线程是不能利用多核的,因为Python虚拟机使用一个全局解释器锁(GIL)来控制线程对 ...
24-Python运行时的环境初始化
24-Python运行时的环境初始化楔子我们之前分析了Python的核心–字节码、以及虚拟机的剖析工作,但这仅仅只是一部分,而其余的部分则被遮在了幕后。记得我们在分析虚拟机的时候,曾这么说过:
当Python启动后,首先会进行 “运行时环境” 的初始化,而关于 “运行时环境” 的初始化是一个非常复杂的过程。并且 “运行时环境” 和 “执行环境” 是不同的, “运行时环境” 是一个全局的概念,而 “执行环境” 是一个栈帧。关于”运行时环境”我们后面将用单独的一章进行剖析,这里就假设初始化动作已经完成,我们已经站在了Python虚拟机的门槛外面,只需要轻轻推动一下第一张骨牌,整个执行过程就像多米诺骨牌一样,一环扣一环地展开。
所以这次,我们将回到时间的起点,从Python的应用程序被执行开始,一步一步紧紧跟随Python的轨迹,完整地展示Python在启动之初的所有动作。当我们根据Python完成所有的初始化动作之后,也就能对Python执行引擎执行字节码指令时的整个运行环境了如执掌了。
线程环境初始化我们知道线程是操作系统调度的最小单元,那么Python中的线程又是怎么样的呢? ...
23-剖析Python中的模块导入机制、Python是如何加载模块的
23-剖析Python中的模块导入机制、Python是如何加载模块的楔子上一篇我们介绍了生成器,本来这里应该介绍协程的,但是大致阅读了一下,感觉如果从源码的角度来介绍协程的话,工作量太大。而且个人精力有限,所以推荐我写的这一篇博客:https://www.cnblogs.com/traditional/p/11828780.html,是用来介绍asyncio的,当然也从Python的角度介绍了Python中的协程。
这一次我们说一下Python模块的加载机制,我们之前所考察的所有内容都具有一个相同的特征,那就是它们都局限在一个py文件中。然而现实中不可能只有一个py文件,而是存在多个,而多个py文件之间存在引用和交互,这些也是程序的一个重要组成部分。那么这里我们就来分析,Python中模块的导入机制。
在这里我们必须强调一点,Python中一个单独的py文件、或者pyd文件,我们称之为一个 *模块* ;而多个模块组合起来放在一个目录中,这个目录我们称之为 *包* 。
但是不管是模块,还是包,它们在Python的底层都是PyModuleObject结构体实例,类型为PyModule_Ty ...
TYPET5 SEQ2SEQ TYPE INFERENCEUSING STATIC ANALYSIS
d9eba399523fe401e434f6396d459a7a735596707e1aa1664e34b3a934e193fb0ea97727a63d29c47c16bc11780e2295230f44148d8f5776b77661d3eaacdc1edb0e7298c625afbed7de1654920dbedcaf6c183075c7c776c41098675090a2e7c3d46c9020485d05f6c0ccfd439ccb33beccc8c7f960b780329fe5815eba98239393b2b73e99e07a6493291a964c054d51c45527d7775054a4e86ab5d210e45920f2e918879e3e25f12eb79920f330cd183c2a31a2776dc246cc5c832f22f6673b02deef89369e00b064861ac42b2f59dfbbbb57b744eebb2d8aa1ccd8075a635f80772d0649f81fd60527221bce66c3a854ae89a86ad999c ...
22-Python中的生成器对象
22-Python中的生成器对象楔子下面我们来聊一聊Python中的生成器,它是我们理解后面协程的基础,生成器的话,估计大部分人在写程序的时候都想不到用。但是一旦用好了,确实能给程序带来性能上的提升,那么我们就来看一看吧。
生成器基本用法我们知道,一个函数如果它的内部出现了yield关键字,那么它就不再是普通的函数了,而是一个生成器函数。当我们调用的时候,就会创建一个生成器对象。
生成器对象一般用于处理循环结构,应用得当的话可以极大优化内存使用率。比如:我们读取一个大文件。
123456def read_file(file): return open(file, encoding="utf-8").readlines()print(read_file("假装是大文件.txt"))# ['人生は一体何だろう\n', 'たぶん 輝いている同時に\n', '人を苦しくさせるものだろう']
这个版本的函数,直接将里面的内容全部读取出来了,返回了一个列表。如果文件非常大,那么内存的开销 ...