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引入了一个内存池机制,用于管理对小块内存的申请和释放。

posted @ 2020-08-04 14:24  ruixing  阅读(135)  评论(0编辑  收藏  举报