Python面试题
is 和 ==的区别
is: 比较对象的id值,也就是是否指向同一个内存地址
==: 比较对象的值是否相等,默认会调用对象的eq()方法
python内置数据结构
列表、字典、字符串、集合、元组
变量作用域
LEGB
L: local函数内部作用域
E: enclosing函数内部与内嵌函数之间
G: global全局作用域
B: build-in 内置作用域
var_a=1
var_b=10
var_c=100
def outer():
var_a=2
var_b=20
def inner():
var_a=3
print(var_a) # L local 优先使用本地
print(var_b) # E enclosing 本地没有找嵌套作用域
print(var_c) # G global 全局作用域
print(__name__,max,min,id) # B built-in 找内置作用域
inner()
outer()
super举例
class Base:
def __init__(self):
print('Base.__init__')
class A(Base):
def __init__(self):
super().__init__()
print('A.__init__')
class B(Base):
def __init__(self):
super().__init__()
print('B.__init__')
class C(A,B):
def __init__(self):
super().__init__() # Only one call to super() here
print('C.__init__')
>>> c = C()
Base.__init__
B.__init__
A.__init__
C.__init__
>>> C.__mro__
(<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class '__main__.Base'>, <class 'object'>)
单例
class Singleton2(object):
def __new__(cls, *args, **kwargs):
if not hasattr(cls, '_instance'):
cls._instance = super(Singleton2, cls).__new__(cls, *args, **kwargs)
return cls._instance
class foo(Singleton2):
pass
foo1 = foo()
foo2 = foo()
print (foo1 is foo2)
字典按值排序
>>> a={"a":2, "v": 1, "ds": 100}
>>> sorted(a.items(), key=lambda x:x[1])
[('v', 1), ('a', 2), ('ds', 100)]
>>> sorted(a.items(), key=lambda x:x[1], reverse=True)
[('ds', 100), ('a', 2), ('v', 1)]
>>> dict(sorted(a.items(), key=lambda x:x[1], reverse=True))
{'ds': 100, 'a': 2, 'v': 1}
找出列表中相同和不同的元素
list1 = [1,2,3]
list2 = [3,4,5]
set1 = set(list1)
set2 = set(list2)
print(set1 & set2) #相同的
print(set1 ^ set2) #不同的
GIL(global interpreter lock - 全局解释器锁)
并行:多个CPU同时执行多个任务,就好像有两个程序,这两个程序是真的在两个不同的CPU内同时被执行。
并发:CPU交替处理多个任务,还是有两个程序,但是只有一个CPU,会交替处理这两个程序,而不是同时执行,只不过因为CPU执行的速度过快,而会使得人们感到是在“同时”执行,执行的先后取决于各个程序对于时间片资源的争夺
- 单线程: 双核cpu使用率50%
- 多线程:双核cpu使用率50%
- 多进程:双核cpu使用率100%
如何解决GIL锁的问题
1、更换Cpython为Jpython
2、使用多进程完成多线程任务
什么时候会释放GIL锁
1、遇到像 i/o操作这种 会有时间空闲情况 造成cpu闲置的情况会释放Gil
2、会有一个专门ticks进行计数 一旦ticks数值达到100 这个时候释放Gil锁 线程之间开始竞争Gil锁(说明:
ticks这个数值可以进行设置来延长或者缩减获得Gil锁的线程使用cpu的时间)
互斥锁和GIL锁的关系
Gil锁 : 保证同一时刻只有一个线程能使用到cpu
互斥锁 : 多线程时,保证修改共享数据时有序的修改,不会产生数据修改混乱
内存管理机制
1、引用计数
引用计数是一种非常高效的内存管理手段,当一个pyhton对象被引用时其引用计数增加1,当其不再被引用时引用计数减1,当引用计数等于0的时候,对象就被删除了。
2、垃圾回收
- 引用计数
- 标记清除
标记清除用来解决循环引用产生的问题,循环引用只有在容器对象才会产生,比如字典,元祖,列表等。首先为了追踪对象,需要每个容器对象维护两个额外的指针,用来将容器对象组成一个链表,指针分别指向前后两个容器对象,这样可以将对象的循环引用摘除,就可以得出两个对象的有效计数。 - 分代回收
随着你的程序运行,Python解释器保持对新创建的对象,以及因为引用计数为零而被释放掉的对象的追踪。从理论上说,创建==释放数量应该是这样子。但是如果存在循环引用的话,肯定是创建>释放数量,当创建数与释放数量的差值达到规定的阈值的时候,当当当当~分代回收机制就登场啦。
分代回收思想将对象分为三代(generation 0,1,2)
0代表幼年对象,
1代表青年对象,
2代表老年对象。
根据弱代假说(越年轻的对象越容易死掉,老的对象通常会存活更久。)
新生的对象被放入0代,如果该对象在第0代的一次gc垃圾回收中活了下来,那么它就被放到第1代里面(它就升级了)。如果第1代里面的对象在第1代的一次gc垃圾回收中活了下来,它就被放到第2代里面。
从上一次第0代gc后,如果分配对象的个数减去释放对象的个数大于threshold0,那么就会对第0代中的对象进行gc垃圾回收检查。
从上一次第1代gc后,如果第0代被gc垃圾回收的次数大于threshold1,那么就会对第1代中的对象进行gc垃圾回收检查。
从上一次第2代gc后,如果第1代被gc垃圾回收的次数大于threshold2,那么就会对第2代中的对象进行gc垃圾回收检查。
gc每一代垃圾回收所触发的阈值可以自己设置。
3、内存池
Python的内存机制呈现金字塔形状,-1,-2层主要有操作系统进行操作
第0层是C中的malloc,free等内存分配和释放函数进行操作
第1层和第2层是内存池,有python接口函数,PyMem_Malloc函数实现,当对象小于256k的时由该层直接分配内存
第3层是最上层,也就是我们对python对象的直接操作
Python在运行期间会大量地执行malloc和free的操作,频繁地在用户态和核心态之间进行切换,这将严重影响Python的执行效率。为了加速Python的执行效 率,Python引入了一个内存池机制,用于管理对小块内存的申请和释放。