一、Python基础题:
-
如何在不创建第三个变量的基础上交换两个变量的值?
a, b = b, a
-
写一个删除列表中重复元素的函数,要求去重后元素相对位置保持不变。
def dedup(items): seen = set() for item in items: if item not in seen: seen.add(item)
-
Lambda函数是什么,举例说明的它的应用场景。这里以对列表中的每个值进行求平方为例来说明
lambda
的用法:items = [12, 5, 7, 10, 8, 19] items = list(map(lambda x: x ** 2,items)) print(items)
-
说说Python中的浅拷贝和深拷贝。解释:浅拷贝通常只复制对象本身,而深拷贝不仅会复制对象,还会递归的复制对象所关联的对象。深拷贝可能会遇到两个问题:一是一个对象如果直接或间接的引用了自身,会导致无休止的递归拷贝;二是深拷贝可能对原本设计为多个对象共享的数据也进行拷贝。Python通过copy模块中的copy和deepcopy函数来实现浅拷贝和深拷贝操作,其中deepcopy可以通过memo字典来保存已经拷贝过的对象,从而避免刚才所说的自引用递归问题;此外,可以通过copyreg模块的pickle函数来定制指定类型对象的拷贝行为。
-
分别用迭代器和生成器实现斐波拉切数列。
# 迭代器 class Fib(object): def __init__(self, num): self.num = num self.a, self.b = 0, 1 self.idx = 0 def __iter__(self): return self def __next__(self): if self.idx < self.num: self.a, self.b = self.b, self.a + self.b self.idx += 1 return self.a raise StopIteration() # 生成器 def fib(num): a, b = 0, 1 for _ in range(num): a, b = b, a + b yield a
-
简单描述一下Python中的内存管理机制。Python GC主要使用引用计数(reference counting)来跟踪和回收垃圾。在引用计数的基础上, 通过“标记-清除”(mark and sweep)解决容器对象可能产生的循环引用问题,通过“分代回收”(generation)以空间换时间的方法提高垃圾回收效率。
-
内存管理&引用计数 python创建新对象都是在内存上开辟一个块, 每个对象只存有一份数据, 赋值和复制都是创建了新的引用, 使用的是对象和引用分离策略 在Python中,每个对象都有存有指向该对象的引用总数,即引用计数, 如果引用计数为0, 那这个对象就会被python垃圾回收机制回收
-
标记-清除机制 基本思路是先按需分配,等到没有空闲内存的时候从寄存器和程序栈上的引用出发,遍历以对象为节点、以引用为边构成的图,把所有可以访问到的对象打上标记,然后清扫一遍内存空间,把所有没标记的对象释放。同时为了保证效率, Python只会在垃圾达到一定阈值时,垃圾回收才会启动。
-
分代回收策略 这一策略的基本假设是,存活时间越久的对象,越不可能在后面的程序中变成垃圾。Python默认定义了三代对象集合,索引数越大,对象存活时间越长。
-
简单描述一下
GIL
全局解释器锁。线程全局锁(Global Interpreter Lock),即Python为了保证线程安全而采取的独立线程运行的限制,说白了就是一个核只能在同一时间运行一个线程。
-
简单描述一下
__new__
和__init__
的区别。创建一个新实例时调用__new__
,初始化一个实例时用__init__
,这是它们最本质的区别。__new__
是一个静态方法,而__init__
是一个实例方法。__new__
方法会返回一个创建的实例,而__init__
什么都不返回。只有在__new__
返回一个cls的实例时后面的__init__
才能被调用。单例模式的实现可以使用__new__
方法。 -
说一下线程和进程的应用场景和优缺点。
-
线程是操作系统分配CPU的基本单位,进程是操作系统分配内存的基本单位。通常我们运行的程序会包含一个或多个进程,而每个进程中又包含一个或多个线程。多线程的优点在于多个线程可以共享进程的内存空间,所以进程间的通信非常容易实现;但是如果使用官方的CPython解释器,多线程受制于GIL(全局解释器锁),并不能利用CPU的多核特性,这是一个很大的问题。使用多进程可以充分利用CPU的多核特性,但是进程间通信相对比较麻烦,需要使用IPC机制(管道、套接字等)。
-
多线程适合那些会花费大量时间在I/O操作上,但没有太多并行计算需求且不需占用太多内存的I/O密集型应用。多进程适合执行计算密集型任务(如:视频编码解码、数据处理、科学计算等)、可以分解为多个并行子任务并能合并子任务执行结果的任务以及在内存使用方面没有任何限制且不强依赖于I/O操作。
-
列举最少5个
Python2
和Python3
的区别。 -
Python3
使用 print 必须要以小括号包裹打印内容,比如print('hi')
Python2
既可以使用带小括号的方式,也可以使用一个空格来分隔打印内容,比如print 'hi'
。 -
python2
中range(1,10)
返回列表,python3
中返回迭代器,节约内存。 -
python2
中使用ascii
编码,python
中使用utf-8
编码。 -
python2
中unicode
表示字符串序列,str
表示字节序列。python3
中str
表示字符串序列,byte
表示字节序列。 -
python2
中为正常显示中文,引入coding
声明,python3
中不需要。 -
python2
中是raw_input()
函数,python3
中是input()
函数。
二、排序算法:
-
冒泡排序:比较相邻的元素。如果第一个比第二个大,就交换他们两个。对第0个到第n-1个数据做同样的工作。这时,最大的数就“浮”到了数组最后的位置上。针对所有的元素重复以上的步骤,除了最后一个。持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。实现代码如下:
# 10000个随机数据耗时16s def maopao(l): for i in range(1, len(l)): for j in range(len(l) - i): if l[j] > l[j + 1]: l[j], l[j + 1] = l[j + 1], l[j] print(l)
-
选择排序:在未排序序列中找到最小(大)元素,存放到排序序列的起始位置。再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。
# 10000个随机数据耗时10s def xuanze(l): for i in range(len(l)): for j in range(i, len(l)): if l[i] > l[j]: l[i], l[j] = l[j], l[i]
-
插入排序:从第一个元素开始,该元素可以认为已经被排序 取出下一个元素,在已经排序的元素序列中从后向前扫描 如果被扫描的元素(已排序)大于新元素,将该元素后移一位 重复步骤3,直到找到已排序的元素小于或者等于新元素的位置 将新元素插入到该位置后 重复步骤2~5。
# 是否正确待确认 # 10000个随机数据耗时10s def charu(l): for i in range(1, len(l)): for j in range(i): if l[j] > l[i]: l[j], l[i] = l[i], l[j] print(l)
-
快速排序:快速排序通常明显比同为Ο(n log n)的其他算法更快,因此常被采用,而且快速排序采用了分治法的思想,所以在很多笔试面试中能经常看到快排的影子。可见掌握快速排序的重要性。从数列中挑出一个元素作为基准数。分区过程,将比基准数大的放到右边,小于或等于它的数都放到左边。再对左右区间递归执行第二步,直至各区间只有一个数。
# 10000个随机数据排序耗时0.05s def quick_sort(ary): return qsort(ary, 0, len(ary) - 1) def qsort(ary, left, right): # 快速排序函数,ary为待排序数组,left为待排序的左边界,right为右边界 if left >= right: return ary key = ary[left] # 取最左边的为基准数 lp = left # 左指针 rp = right # 右指针 while lp < rp: # 如果左指针在右指针右边就死循环 while ary[rp] >= key and lp < rp: # 拿右指针上的值和key比较, 如果比key大,就向左移指针,直至条件不再成立 rp -= 1 while ary[lp] <= key and lp < rp: lp += 1 ary[lp], ary[rp] = ary[rp], ary[lp] # 对调左右指针上的值 # print(ary, 'key:%s lp:%s rp:%s' % (key, lp, rp)) ary[left], ary[lp] = ary[lp], ary[left] # 由上条件知道此时左指针值比key小,对调,下次循环基准数更新 qsort(ary, left, lp - 1) qsort(ary, rp + 1, right) return ary