day01
1.深浅拷贝
浅拷贝:对于浅拷贝,字典、列表、元组等类型,它们只拷贝第一层地址
深拷贝:对于深拷贝,字典、列表、元组等类型,它里面嵌套多少层,就会拷贝多少层出来,但是最底层的数字和字符串地址不变
2.垃圾回收机制
python的垃圾回收有三种策略
- 引用计数
- 分代回收
- 标记清除
引用计数:PyObject是每个对象必有的内容,其中ob_refcnt就是做为引用计数。当一个对象有新的引用时,它的ob_refcnt就会增加,当引用它的对象被删除,它的ob_refcnt就会减少.引用计数为0时,该对象生命就结束了
分代回收:将系统中的所有内存块根据其存活时间划分为不同的集合,每个集合就成为一个“代”,垃圾收集频率随着“代”的存活时间的增大而减小,存活时间通常利用经过几次垃圾回收来度量。
标记清除:基本思路是先按需分配,等到没有空闲内存的时候从寄存器和程序栈上的引用出发,遍历以对象为节点、以引用为边构成的图,把所有可以访问到的对象打上标记,然后清扫一遍内存空间,把所有没标记的对象释放。
3.迭代器和生成器的区别
迭代器:是一个更加抽象的概念,任何对象,如果它的类有next方法和iter方法返回自身。对于string、list、dict、tuple等这类容器对象,使用for循环遍历是很方便的。在后台for语句对容器对象调用iter()函数,iter()是Python的内置函数。iter()会返回一个定义了next()方法的迭代器对象,它在容器中逐个访问容器内元素,next()也是python的内置函数。在没有后续元素时,next()会抛出一个StopIterration的异常。
生成器:是创建迭代器的简单而强大的工具。它们写起来就像是正规的函数,只是在返回数据的时候需要使用yield语句。每次next()被调用时,生成器会返回它脱离的位置(它记忆语句最后一次执行的位置和所有的数据值)
总结:生成器能做到迭代器能做的所有事,而且因为自动创建了__iter__()和next()方法,生成器显得特别简洁,而且生成器也是高效的,使用生成器表达式取代列表解析可以同时节省内存。除了创建和保持程序状态的自动生成,当发生器终结时,还会自动跑出StopIterration异常。
4.装饰器
装饰器就是在不改变被装饰对象原来调用方式及内部原代码的基础上添加新的概念而已,装饰器不是新的技术点,就是利用函数对象,闭包函数等知识拼凑出来的一个工具而已,我写的比较多的装饰器都是无参装饰器,最复杂的也就是有参装饰器了,就是给装饰器外部再包一层,最多最多也就三层而已
5.元类
元类其实就是产生类的类,我们可以通过元类来拦截类的创建过程,这个地方我自己通过元类写过一个简易版本的ORM
首先ORM全称叫对象关系映射,能够让不会数据库操作的程序员通过面向对象的方法简单快捷的操作数据库,ORM有三层映射关系
- 类映射数据库的表
- 对象映射成数据库的表中的一条条记录
- 对象获取属性映射成数据库的表中的某条记录某个字段对应的值
具体做法就是在类创建过程中通过元类拦截它的创建,在类创建出来之前给类赋上表该有的属性表名,主键字段,其他普通字段
6.简单谈下GIL
Python代码的执行由python虚拟机(也叫解释器主循环,CPython版本)来控制,Python在设计之初就考虑到要在解释器的主循环中,同时只有一个线程在执行,即任意时刻,只有一个线程在解释器中运行。对Python虚拟机的访问由全局解释器锁(GIL)来控制,正是这个锁能保证同一时刻只有一个线程在运行。
在多线程环境中,Python虚拟机按以下方式执行:
1.设置GIL
2.切换到一个线程去运行
3.运行:
a.指定数量的字节码指令,或者
b.线程主动让出控制(可以调用time.sleep(0))
4.把线程设置为睡眠状态
5.解锁GIL
6.再次重复以上所有步骤
再调用外部代码(如C/C++扩展函数)的时候,GIL讲会被锁定,直到这个函数结束为止(由于在这期间没有Python的字节码被运行,所以不会做线程切换)
7.IO模型
首先我接触到的IO模型大致有以下五种类型
- blocking IO 阻塞IO
- nonblocking IO 非阻塞IO
- IO multiplexing IO多路复用
- signal driven IO 信号驱动IO
- asynchronous IO 异步IO
其中signal driven IO(信号驱动IO)实际工作中不常用,我没有做了解
然后要想研究IO模型,首先需要先搞明白两组概念
同步与异步
"""
这一组概念描述的任务的提交方式
同步:提交任务之后原地等待任务的返回结果,期间不做任何事情,给人的感觉就是卡顿住了
异步:提交任务之后不原地等待任务的返回结果,而是继续执行下一行代码
"""
阻塞与非阻塞
"""
这一组概念描述的是程序的运行状态
阻塞:程序处于阻塞态,要想再次运行,必须先完成所有IO操作然后处于就绪态等待被CPU执行
非阻塞:程序处于就绪态或者运行态
"""
阻塞IO
blocking IO的特点就是在IO执行的两个阶段(等待数据和拷贝数据两个阶段)都被block了
非阻塞IO
用户进程需要不断的主动询问kernel数据准备好了没有,这种模型是绝不被推荐的,不断的主动询问会大幅度推高CPU占用率,并且任务完成的响应延迟增大,因为每过一段时间才去轮询一次read操作,而任务可能在两次轮询之间的任意时间完成。这会导致整体数据吞吐量的降低。
IO多路复用
使用select/epoll监听监管的任务,只用单线程(进程)执行,占用资源少,不消耗太多 CPU,同时能够为多客户端提供服务。优势在于处理多个链接不适合处理单个链接
异步IO
用户进程发起read操作之后,立刻就可以开始去做其它的事。而另一方面,从kernel的角度,当它受到一个asynchronous read之后,首先它会立刻返回,所以不会对用户进程产生任何block。然后,kernel会等待数据准备完成,然后将数据拷贝到用户内存,当这一切都完成之后,kernel会给用户进程发送一个signal,告诉它read操作完成了。