面试必问-Python篇(二)

112 阅读7分钟

一、前言

本专栏将会对近年高频测试相关面试题做详细解答,结合自己多年工作经验,以及同行大佬指导总结出来的。旨在帮助测试、python方面的同学,顺利通过面试,拿到自己满意的offer。

本期题目相对于之前有一点的难度,对于原理题直接背就行了,不需要太去了解它的原理,因为面试官也不一定懂。

让我们一起卷起来吧!

二、 试题详解

1)map函数运用

给定一个列表,求其元素的平方根并返回一个列表,例如:[1, 2, 3, 4, 5]得到[1, 4, 9, 16, 25]:

第一种方法:

a_list = [1, 2, 3, 4, 5]

def square(x):  # 计算平方数
    return x ** 2

res = map(square, a_list)
print(list(res))

第二种:使用匿名函数:

a_list = [1, 2, 3, 4, 5]
res = map(lambda x: x ** 2, a_list)
print(list(res))

输出结果

[1, 4, 9, 16, 25]

2)给定两个列表,怎么找出他们相同的元素和不同的元素

list1 = [1,2,3]
list2 = [3,4,5]
set1 = set(list1)
set2 = set(list2)
print(set1 & set2)
print(set1 ^ set2)

输出结果

{3}
{1, 2, 4, 5}

3)Python垃圾回收及分代回收,如何强制回收和禁用垃圾回收?

垃圾回收

Python语言默认采用的垃圾收集机制是“引用计数法 Reference Counting”。 Python的垃圾回收是建立在引用技术上的,所以理解引用计数也是非常重要的。而引用计数的原理就是,当一个对象的引用被创建或者复制时,对象的引用计数加1;当一个对象的引用被销毁时,对象的引用计数减1;当对象的引用计数减少为0时,就意味着对象已经没有被任何人使用了,可以将其所占用的内存立刻释放了。

它的缺点是需要额外的空间维护引用计数,这个问题是其次的,不过最主要的问题是它不能解决对象的“循环引用”,因此,也有很多语言比如Java并没有采用该算法做来垃圾的收集机制。

Python中一切皆对象,也就是说,在Python中你用到的一切变量,本质上都是类对象。实际上每一个对象的核心就是一个结构体PyObject,它的内部有一个引用计数器ob_refcnt,程序在运行的过程中会实时的更新ob_refcnt的值,来反映引用当前对象的名称数量。当某对象的引用计数值为0,说明这个对象变成了垃圾,那么它会被回收掉,它所用的内存也会被立即释放掉。


分代回收

分代回收是在面试中,常常会被问到的一个问题。分代回收的核心思想就是,对象存活的时间越长,越不可能是垃圾,应该更少的去回收。且Python将所有的对象分为0、1、2三代,所有的新建对象都是0代对象。但是,当某一代对象经历过垃圾回收,依然存活,那么它就被归入下一代对象,即1代或者2代了。分代回收的预值,可以使用如下代码进行查看。通常,返回一个元组且包含三个数值,默认值为(700, 10, 10)。其中第一个数值700表示,从上一个垃圾回收到现在分配内存的数目减去释放内存的数目。如果这个数值到了700,则会对第一代的垃圾对象进行回收,并且给第二个数值加1。当第二个数值增加到10的时候,就会对第一代和第二代的垃圾对象进行回收,并且给第三个数值加1。当第三个数值增加到10的时候,则三代都会被回收,然后初始化为(0, 0, 0)并继续开始计数。需要注意的是,如果没有十分必要的场景,这个分代回收的默认值通常是不需要我们人为的改动的。


强制回收

Python也支持在某一刻特定的时间点,使用gc.collect()方法强制回收。不过,通常我们是不适用强制回收的,而是使用下面这种禁用垃圾回收的方式。


禁用垃圾回收

这个垃圾回收机制不是挺好的,那我们会什么还要禁用呢。通常我们禁用GC的一个场景就是,某一段代码中需要加载大量的原始数据,尤其是有大量的新建、删除对象这样的操作。也就是执行某一段代码的时候,会自动触发很多次的垃圾回收。但是,我们需要知道Python执行垃圾回收的时候,它会暂停当前的工作。所以,这种工作耗时越多就会拖累我们程序的运行时间。那我们怎么办呢?我们通常都会在执行这段代码之前,禁用垃圾回收,执行完之后再手动开启。熟悉开源项目的同学可以会看到,有些项目中会使用gc.set_threshold(0)而不用gc.disable这种写法。是因为有些第三方的库会隐式的启用GC让gc.disable不起作用了,而使用gc.set_threshold(0)就不会有第三方的库把垃圾回收开启了,除非我们想要把它开启。


4)Python字典原理

在 Python中,字典是通过散列表或说哈希表实现的。字典也被称为关联数组,还称为哈希数组等。也就是说,字典也是一个数组,但数组的索引是键经过哈希函数处理后得到的散列值。哈希函数的目的是使键均匀地分布在数组中,并且可以在内存中以O(1)的时间复杂度进行寻址,从而实现快速查找和修改。哈希表中哈希函数的设计困难在于将数据均匀分布在哈希表中,从而尽量减少哈希碰撞和冲突。由于不同的键可能具有相同的哈希值,即可能出现冲突,高级的哈希函数能够使冲突数目最小化。 Python中并不包含这样高级的哈希函数,几个重要(用于处理字符串和整数)的哈希函数是常见的几个类型。通常情况下建立哈希表的具体过程如下数据添加:把key通过哈希函数转换成一个整型数字,然后就将该数字对数组长度进行取余,取余结果就当作数组的下标,将 value存储在以该数字为下标的数组空间里。数据查询:再次使用哈希函数将key转换为对应的数组下标,并定位到数组的位置获取 value。


5)“is”和“==”有什么区别

is:比较的是两个对象的id值是否相等,也就是比较俩对象是否为同一个实例对象。是否指向同一个内 存地址

== : 比较的两个对象的内容/值是否相等,默认会调用对象的eq()方法


6)一行代码实现1-100之和

这里直接使用Python内置函数sum即可:

print(sum(range(0,101)))

输出结果

5050

7)反转一个整数,例如-123 --> -321

class Solution(object):
    def reverse(self, x):
        if -10 < x < 10:
            return x
        str_x = str(x)
        if str_x[0] != "-":
            str_x = str_x[::-1]
            x = int(str_x)
        else:
            str_x = str_x[1:][::-1]
            x = int(str_x)
            x = -x
        return x if -2147483648 < x < 2147483647 else 0


if __name__ == '__main__':
    s = Solution()
    reverse_int = s.reverse(-123)
    print(reverse_int)

输出结果

-321

8)设计实现遍历目录与子目录,抓取.pyc文件

第一种方法:

import os


def get_files(dir, suffix):
    res = []
    for root, dirs, files in os.walk(dir):
        for filename in files:
            name, suf = os.path.splitext(filename)
            if suf == suffix:
                res.append(os.path.join(root, filename))
    print(res)


get_files("./", '.pyc')

第二种方法:

import os

def pick(obj):
    if obj.endswith(".pyc"):
        print(obj)

def scan_path(ph):
    file_list = os.listdir(ph)
    for obj in file_list:
        if os.path.isfile(obj):
            pick(obj)
        elif os.path.isdir(obj):
            scan_path(obj)

if __name__ == '__main__':
    path = input('输入目录')
    scan_path(path)

第三种方法:

from glob import iglob

def func(fp, postfix):
    for i in iglob(f"{fp}/**/*{postfix}", recursive=True):
        print(i)

if __name__ == "__main__":
    postfix = ".pyc"
    func("K:\Python_script", postfix)

三、总结

本期题目难度适中,预计消化时间为一周左右,不要松懈!卷起来!保持热情!