题目36:如何使用random
模块生成随机数、实现随机乱序和随机抽样?
点评:送人头的题目,因为Python标准库中的常用模块应该是Python开发者都比较熟悉的内容,这个问题回如果答不上来,整个面试基本也就砸锅了。
random.random()
函数可以生成[0.0, 1.0)
之间的随机浮点数。random.uniform(a, b)
函数可以生成[a, b]
或[b, a]
之间的随机浮点数。random.randint(a, b)
函数可以生成[a, b]
或[b, a]
之间的随机整数。random.shuffle(x)
函数可以实现对序列x
的原地随机乱序。random.choice(seq)
函数可以从非空序列中取出一个随机元素。random.choices(population, weights=None, *, cum_weights=None, k=1)
函数可以从总体中随机抽取(有放回抽样)出容量为k
的样本并返回样本的列表,可以通过参数指定个体的权重,如果没有指定权重,个体被选中的概率均等。random.sample(population, k)
函数可以从总体中随机抽取(无放回抽样)出容量为k
的样本并返回样本的列表。
扩展:random
模块提供的函数除了生成均匀分布的随机数外,还可以生成其他分布的随机数,例如random.gauss(mu, sigma)
函数可以生成高斯分布(正态分布)的随机数;random.paretovariate(alpha)
函数会生成帕累托分布的随机数;random.gammavariate(alpha, beta)
函数会生成伽马分布的随机数。
题目37:解释一下线程池的工作原理。
线程池是一种用于减少线程本身创建和销毁造成的开销的技术,属于典型的空间换时间操作。如果应用程序需要频繁的将任务派发到线程中执行,线程池就是必选项,因为创建和释放线程涉及到大量的系统底层操作,开销较大,如果能够在应用程序工作期间,将创建和释放线程的操作变成预创建和借还操作,将大大减少底层开销。线程池在应用程序启动后,立即创建一定数量的线程,放入空闲队列中。这些线程最开始都处于阻塞状态,不会消耗CPU资源,但会占用少量的内存空间。当任务到来后,从队列中取出一个空闲线程,把任务派发到这个线程中运行,并将该线程标记为已占用。当线程池中所有的线程都被占用后,可以选择自动创建一定数量的新线程,用于处理更多的任务,也可以选择让任务排队等待直到有空闲的线程可用。在任务执行完毕后,线程并不退出结束,而是继续保持在池中等待下一次的任务。当系统比较空闲时,大部分线程长时间处于闲置状态时,线程池可以自动销毁一部分线程,回收系统资源。基于这种预创建技术,线程池将线程创建和销毁本身所带来的开销分摊到了各个具体的任务上,执行次数越多,每个任务所分担到的线程本身开销则越小。
一般线程池都必须具备下面几个组成部分:
- 线程池管理器:用于创建并管理线程池。
- 工作线程和线程队列:线程池中实际执行的线程以及保存这些线程的容器。
- 任务接口:将线程执行的任务抽象出来,形成任务接口,确保线程池与具体的任务无关。
- 任务队列:线程池中保存等待被执行的任务的容器。
题目38:举例说明什么情况下会出现KeyError
、TypeError
、ValueError
。
举一个简单的例子,变量a
是一个字典,执行int(a['x'])
这个操作就有可能引发上述三种类型的异常。如果字典中没有键x
,会引发KeyError
;如果键x
对应的值不是str
、float
、int
、bool
以及bytes-like
类型,在调用int
函数构造int
类型的对象时,会引发TypeError
;如果a[x]
是一个字符串或者字节串,而对应的内容又无法处理成int
时,将引发ValueError
。
题目39:说出下面代码的运行结果。
def extend_list(val, items=[]):
items.append(val)
return items
list1 = extend_list(10)
list2 = extend_list(123, [])
list3 = extend_list('a')
print(list1)
print(list2)
print(list3)
点评:Python函数在定义的时候,默认参数items
的值就被计算出来了,即[]
。因为默认参数items
引用了对象[]
,每次调用该函数,如果对items
引用的列表进行了操作,下次调用时,默认参数还是引用之前的那个列表而不是重新赋值为[]
,所以列表中会有之前添加的元素。如果通过传参的方式为items
重新赋值,那么items
将引用到新的列表对象,而不再引用默认的那个列表对象。
[10, 'a']
[123]
[10, 'a']
题目40:如何读取大文件,例如内存只有4G,如何读取一个大小为8G的文件?
很显然4G内存要一次性的加载大小为8G的文件是不现实的,遇到这种情况必须要考虑多次读取和分批次处理。在Python中读取文件可以先通过open
函数获取文件对象,在读取文件时,可以通过read
方法的size
参数指定读取的大小,也可以通过seek
方法的offset
参数指定读取的位置,这样就可以控制单次读取数据的字节数和总字节数。除此之外,可以使用内置函数iter
将文件对象处理成迭代器对象,每次只读取少量的数据进行处理,代码大致写法如下所示。
with open('...', 'rb') as file:
for data in iter(lambda: file.read(2097152), b''):
pass
在Linux系统上,可以通过split
命令将大文件切割为小片,然后通过读取切割后的小文件对数据进行处理。例如下面的命令将名为filename
的大文件切割为大小为512M的多个文件。
split -b 512m filename
如果愿意, 也可以将名为filename
的文件切割为10个文件,命令如下所示。
split -n 10 filename
扩展:外部排序跟上述的情况非常类似,由于处理的数据不能一次装入内存,只能放在读写较慢的外存储器(通常是硬盘)上。“ 排序-归并算法”就是一种常用的外部排序策略。在排序阶段,先读入能放在内存中的数据量,将其排序输出到一个临时文件,依此进行,将待排序数据组织为多个有序的临时文件,然后在归并阶段将这些临时文件组合为一个大的有序文件,这个大的有序文件就是排序的结果。 温馨提示:Python面试宝典会持续更新,从基础到项目实战的内容都会慢慢覆盖到。虽然每天只更新5个题目,但是每道题扩散出的信息量还是比较大的,希望对找工作的小伙伴所有帮助。 你的点赞、收藏和评论都是我继续创建的动力,请不要吝惜你的赞美。