python 原理
python 的垃圾回收机制
- 引用计数
每个python对象都有一个核心的结构结构体PyObject, PyObject是每个对象必有的内容,其中ob_refcnt就是做为引用计数。当一个对象有新的引用时,它的ob_refcnt就会增加,当引用它的对象被删除,它的ob_refcnt就会减少。
优点:
- 简单
- 实时性:一旦没有引用,内存就直接释放了。不用像其他机制等到特定时机。实时性还带来一个好处:处理回收内存的时间分摊到了平时。
缺点: - 维护引用计数消耗资源
- 循环引用
- 分代回收
分代回收是一种以空间换时间的操作方式,Python将内存根据对象的存活时间划分为不同的集合,每个集合称为一个代,Python将内存分为了3“代”,分别为年轻代(第0代)、中年代(第1代)、老年代(第2代),他们对应的是3个链表,它们的垃圾收集频率与对象的存活时间的增大而减小。
新创建的对象都会分配在年轻代,年轻代链表的总数达到上限时,Python垃圾收集机制就会被触发,把那些可以被回收的对象回收掉,而那些不会回收的对象就会被移到中年代去,依此类推,老年代中的对象是存活时间最久的对象,甚至是存活于整个系统的生命周期内。
同时,分代回收是建立在标记清除技术基础之上。分代回收同样作为Python的辅助垃圾收集技术处理那些容器对象
如果存在循环引用的话,肯定是创建>释放数量,当创建数与释放数量的差值达到规定的阈值的时候
- 标记清除
- 标记阶段,遍历所有的对象,如果是可达的(reachable),也就是还有对象引用它,那么就标记该对象为可达;
- 清除阶段,再次遍历对象,如果发现某个对象没有标记为可达,则就将其回收
每个对象有一个gc_count的副本叫gc_ref, 对所有的gc_ref 减一,把所有gc_ref = 0 的分到unreachable 小组, 再遍历 reachable小组里的对象,把引用到的标记为reachable, 从unreachable 中拿出, 这样循环引用的两个就丢到 unreachable中,被释放了。
python 实现单例
class Singleton(object):
_instance = None
def __new__(cls, *args, **kwargs):
if cls._instance is None:
cls._instance = object.__new__(cls, *args, **kwargs)
return cls._instance
浅拷贝和深拷贝
浅拷贝仅仅复制了容器中元素的地址
深拷贝,完全拷贝了一个副本,容器内部元素地址都不一样
python 的闭包
python中闭包从表现形式上看,如果在一个内部函数里,对在外部作用域(但不是在全局作用域)的变量进行引用,那么内部函数就被认为是闭包(closure)
def outer(x):
def inner(y):
return x + y
return inner
闭包可以调用外部变量,但是无法修改,外部变量
def outer():
s = 1
print('outer', s)
def inner():
# s = s + 1 不可以修改外部变量,除非使用容器对象如 list,dict
a = s +1
print('inner',a)
inner()
return
python 中的堆栈
栈区:存储运行方法的形参、局部变量、返回值。由系统自动分配和回收。
堆区:new一个对象的引用或地址存储在栈区,指向该对象存储在堆区中的真实数据。
栈(操作系统):由操作系统自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。
堆(操作系统): 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收,分配方式倒是类似于链表。
栈使用的是一级缓存, 他们通常都是被调用时处于存储空间中,调用完毕立即释放。
堆则是存放在二级缓存中,生命周期由虚拟机的垃圾回收算法来决定(并不是一旦成为孤儿对象就能被回收)。所以调用这些对象的速度要相对来得低一些。
Flask
Flask 中请求钩子的理解?
为了让每个视图函数避免编写重复功能的代码,Flask提供了通用设施的功能,请求钩子。
请求钩子是通过装饰器的形式实现的,支持以下四种:
1.before_first_request 在处理第一个请求前运行
2.before_request:在每次请求前运行
3.after_request:如果没有未处理的异常抛出,在每次请求后运行
4.teardown_request:即使有未处理的异常抛出,在每次请求后运行
请求在flask中过程简述
- 创建请求上下文
- 创建应用上下文
- 把上下文压入栈
- 执行请求钩子 before_first_request 的相关操作
- 执行请求钩子 before_request 的相关操作
- 路由
- 执行请求钩子 after_request 的相关操作
- 执行请求钩子 teardown_request 的相关操作
- 把上下文弹出栈
- 返回响应结果
链接: 跳转
python 底层结构实现
dict 实现
entries = [
(hash(key), key , value),
…
]
存储的是一个连续内存,新的key-value进来, 先进行hash, 填入队友的坑。
- 若index下标位置已经被占用,则会判断enteies的key是否与要插入的key是否相等
- 如果key相等就表示key已存在,则更新value值
- 如果key不相等,就表示hash冲突,则会继续向下寻找空位置,一直到找到剩余空位为止
3.7 版本后,添加了一个list , 存储 index, 这样,dict 就变成有序的了。
下面为具体的实现过程:
- 计算key的hash值【hash(key)】,再和mask做与操作【mask=字典最小长度(IndicesDictMinSize) - 1】,运算后会得到一个数字【index】,这个index就是要插入的indices的下标位置(注:具体算法与Python版本相关,并不一定一样
- 得到index后,会找到indices的位置,但是此位置不是存的hash值,而是存的len(enteies),表示该值在enteies中的位置
- 如果出现hash冲突,则处理方式与老字典处理方式类似
python 和 GO 的 协程 区别
python
- 单线程内切换,适用于IO密集型程序中,可以最大化IO多路复用的效果。
- 无法利用多核。
- 协程间完全同步,不会并行。不需要考虑数据安全。
- 用法多样,可以用在web服务中,也可用在pipeline数据/任务消费中
Go
- 协程间需要保证数据安全,比如通过channel或锁。
- 可以利用多核并行执行。
- 协程间不完全同步,可以并行运行,具体要看channel的设计。
- 抢占式调度,可能无法实现公平