机器学习面经 - python基础
一、简介
Python 面试基础知识点涵盖了语言特性、数据类型、控制结构、函数、面向对象编程、异常处理、模块与包、文件操作等方面的内容。
二、面经
1、你认为好的代码应该是怎样的?
2、python有哪些数据类型?
3、python列表和链表的区别?
4、python堆和栈的区别?
5、python链表都有哪些?特点是什么,应用场景?
6、python其他数据结构有哪些?
7、说一下map用的数据结构?
8、解决哈希冲突的几种方法?
9、给你一个字符串,怎么判断它是整数?
10、面向对象的特点有哪些?
11、python设计模式有哪些?
12、哈希表更像是一种列表还是链表?
13、python 中列表中的 del,remove,和 pop 等的用法和区别?
14、python yeild 和return的区别?
15、python 深拷贝与浅拷贝
16、python多线程能用多个cpu么?python中多线程和多进程?
17、python的垃圾回收机制,(python是如何实现内存管理的?
18、python中的生成器是什么?说说你对生成器和迭代器、装饰器的理解,区别?
19、Lambda函数是什么,举例说明的它的应用场景。
20、python中为什么没有函数重载?
21、写一个函数统计传入的列表中每个数字出现的次数并返回对应的字典。
22、使用Python代码实现遍历一个文件夹的操作。
23、__init__和__new__方法有什么区别?
24、函数参数*arg和**kwargs分别代表什么?
25、说一下python中变量的作用域。
26、说一下你对闭包的理解
27、谈谈你对“猴子补丁”的理解。
28、python中如何实现字符串替换操作?
29、如何使用random模块生成随机数、实现随机乱序和随机抽样?
30、举例说明什么情况下会出现KeyError、TypeError、ValueError。
31、如何读取大文件,例如内存只有4G,如何读取一个大小为8G的文件?
32、说一下你对python中模块和包的理解。
33、正则表达式中的match和search方法
34、Python 中的 is 和 == 有什么区别?
35、当退出 Python 时是否释放所有内存分配?
36、python Set的底层实现?
37、__init__.py 文件的作用以及意义
持续更新中
三、面经参考回答
1、你认为好的代码应该是怎样的?
参考回答:这个问题网络上有着很好的一些规范,而且在python中也有很多推荐的风格如PEP8等,在我看来,抛除基本的功能实现和鲁棒性,好的代码应该有三个特点:高可读性,低代码量,高扩展度。高可读性要求写的代码其他人拿过去要能看的懂,现在都是协作做项目,很多时候需要代码的交流,注释,变量和方法的命名要有规范性,不能让别人产生混淆或者搞不清在做什么;低代码量表示代码要尽量简练一些,一个功能可以10行实现,就不要写100行,代码越长,无论是debug还是阅读都很困难;第三个高扩展度也是从协作的角度说的,写代码的时候尽量模块化接口化,让别人在整合代码的时候容易上手,而不需要做大量适配性的工作。
2、python有哪些数据类型?
参考回答:数字,字符串,列表,元组,字典,集合。其中number,string,tuple 是不可变类型,list,set,dic是可变类型。
3、python列表和链表的区别?
参考回答:在数据结构特点上,列表是一种从栈中分配空间的数据结构,在使用时需要初始化大小,并大小始终固定,访问时依下标对元素进行访问,插入删除操作比较复杂,需要变动很多元素;链表是一种从堆中分配空间的数据结构,在使用时无需初始化,空间是随用随开的,长度也是可变的,访问时需要从头开始通过next指针进行遍历性访问,插入删除操作简单,只需要变更指针指向。在使用场景上,列表适合需要快速访问的场景,比如一个公司的所有员工的通讯录,而链表适合需要频繁插入删除操作的场景,比如存在优先级的订单队列。
4、python堆和栈的区别?
参考回答:一、栈一种先进后出的数据结构。堆可以被看成是一棵树,如:堆排序;二、从空间分配区别:栈使用的是一级缓存,栈由操作系统自动分配释放,存放函数的参数值,局部变量的值等,但是当局部数组过大,当函数内部的数组过大时,有可能导致堆栈溢出,在递归调用层次太多。递归函数在运行时会执行压栈操作,当压栈次数太多时,也会导致堆栈溢出。他们通常都是被调用时处于存储空间中,调用完毕立即释放;堆是存放在二级缓存中,生命周期由垃圾回收算法来决定(并不是一旦成为孤儿对象就能被回收),堆一般由程序员分配释放,若程序员不释放,程序结束时可能由操作系统回收。
5、python链表都有哪些?特点是什么,应用场景?
参考回答:常见的有单链表、双向链表、循环链表。单链表的每个节点只包含一个后继指针;头结点用来记录链表的基地址,是链表遍历的起点,尾结点的后继指针不指向任何结点,而是指向一个空地址NULL。单链表的插入、删除操作时间复杂度为O(1),随机查找时间复杂度为O(n)。循环列表是一种特殊的单链表,它跟单链表唯一的区别就在于它的尾结点又指回了链表的头结点,首尾相连,形成了一个环,所以叫做循环链表。与单链表相比,循环链表的优点是从链尾到链首比较方便,适用于处理具有环形结构的数据问题,比如著名的约瑟夫问题。双向链表中的每个结点具有两个方向指针,后继指针(next)指向后面的结点,前驱指针(prev)指向前面的结点。双向链表也有两个特殊结点,首节点的前驱指针和尾结点的后继指针均指向空地址NULL。与单链表相比,储存同样的数据,双向链表会占用更多的内存空间。
6、python其他数据结构有哪些?
参考回答:栈是一种数据集合,可以用列表实现,可以理解为只能在一端插入和删除的列表。栈的特点是后进先出,有一些基本的操作:如push进栈,pop弹出栈顶元素。队列:队列也是一种数据集合,可用列表实现,仅允许在列表的一端进行插入,一端进行删除。队列的特点是先进先出。哈希表:哈希表是一种线性存储的结构,由直接寻值表和一个哈希函数组成,哈希表是通过哈希函数来计算数据存储位置的数据结构。它支持命令,insert(key, value)插入键值对,get(key)取值,delete(key)删除某个键值对。
7、说一下map用的数据结构?
参考回答:在python中没有map中变量类型,我想您应该是说的C++中标准库中的map,它在python中对应的是orderedDict,其实现方式是红黑树,还有一种是无序的map,在python中是dict字典的形式,实现方式是哈希表。集合set的底层实现方式也是哈希表。在python的dict(set)中间进行查找某个key操作时,查找所需时间不会随着dict(set)中键值对数量增多而变长,(时间复杂度为O(1))但是list中就会(时间复杂度为O(N)),这是因为list查询实现的方式是循环遍历所有列表,然后查找对应的元素,所以列表中元素越多,查找越费时间,但是同一个dict中的所有key的id在内存中是连续的,并且其数据的存储方式为hash表的形式。在set,dic中存数的时候,首先会根据key进行hash映射到对应的表中元素,然后再对应的表元中开辟内存,存入数据,当如果存在不同的两个key的hash结果相同的时候,就会使用散列值的另一部分来定位散列表中的另一行。在dict中查找指定的key时,会先计算key的散列值,然后使用散列值的一部分来定位表元,如果没有找到相应的表元,则说明dict中不存在对应的key报出KeyError异常。如果找到表元之后,会判断表元中的key是否和要查找的key相等,相等就返回对应值,如果不相等则使用其对应的散列值的其他部分来定位散列表中的其他行。
8、解决哈希冲突的几种方法?
参考回答:(1)开放寻址法 :这种方法也称再散列法,其基本思想是:当关键字key的哈希地址p=H(key)出现冲突时,以p为基础,产生另一个哈希地址p1,如果p1仍然冲突,再以p为基础,产生另一个哈希地址p2,…,直到找出一个不冲突的哈希地址pi ,将相应元素存入其中;(2)拉链法:基本思想是将所有哈希地址为i的元素构成一个称为同义词链的单链表,并将单链表的头指针存在哈希表的第i个单元中,因而查找、插入和删除主要在同义词链中进行。链地址法适用于经常进行插入和删除的情况。
9、给你一个字符串,怎么判断它是整数?
参考回答:Python自带很多内置函数都可以:string.isalnum(),string.isdigit()等
10、面向对象的特点有哪些?
参考回答:封装、继承、多态。 封装是我们从业务逻辑中抽象对象时,要赋予对象相关数据与操作,将一些数据和操作打包在一起的过程。封装是使用对象的主要魅力之一,使得对象的实现与使用是相互独立的,封装的另外一个优势是支持代码复用,它可以将常用功能以组件方式打包起来。多态意味着多种形式,多态的作用可以让我们在不同情况下用一个函数名启用不同的方法。多态举例:在屏幕上有一个图形对象列表objects,包括circle,rectangle,polygon等,我们当然可以使用一套代码完成上诉的每一个图像功能。但是多态让我们使用一个函数名,用相同的代码,可以画出列表中所有的图形。多态给予了面向对象系统极大的灵活性。面向对象的编程带来的主要好处之一是代码的重用,实现这种重用的方法之一是通过继承机制。通过继承创建的新类称为子类或派生类,被继承的类称为基类、父类或超类。如果在子类中需要父类的构造方法可以直接显式的调用父类的构造方法,或者不重写父类的构造方法。在python调用基类的方法时,需要加上基类的类名前缀,且需要带上 self 参数变量。
11、python设计模式有哪些?
参考回答:主要可分为三种类别,包括创建型的单例模式、工厂模式等,还有一些结构型的模式,还有一些行为型的模式。总共有十几二十种,我没有全部都用过,就用过单例模式,它保证一个类中只有一个实例,并且提供一个访问它的全局访问点,单例相当于全局变量。应用场景:python的日志、网站计数器。
12、哈希表更像是一种列表还是链表?
参考回答:哈希表是两种数据结构的组合,哈希表本身更像是一种列表,但是哈希表存在哈希冲突,而普遍用来解决哈希冲突的拉链法则是用链表的方式实现的,所以说它是两者的组合。
13、python 中列表中的 del,remove,和 pop 等的用法和区别?
参考回答:pop:value = List.pop(index)
1、pop按照索引位置删除元素
2、无参数时默认删除最后一个元素
3、返回删除的元素值
remove
1、remove 按照值删除,删除单个元素
2、删除首个符合条件的元素
3、返回值为空 None
del
根据索引位置来删除单个值或指定范围内的值。
del是删除引用(变量)而不是删除对象(数据),对象由自动垃圾回收机制(GC)删除
14、python yeild 和return的区别?
参考回答:return和yield都用来返回值;在一次性地返回所有值场景中return和yield的作用是一样的。不同点:如果要返回的数据是通过for等循环生成的迭代器类型数据(如列表、元组),return只能在循环外部一次性地返回,yeild则可以在循环内部逐个元素返回。
15、python 深拷贝与浅拷贝?
参考回答:浅拷贝通常只复制对象本身,假设a浅拷贝了b,虽然a和b都是独立的对象,但是它们的子对象还是指向统一对象,相当于引用,例如列表的切片操作[:];相当于实现了列表对象的浅拷贝,Python一般通过copy模块中的copy函数来实现浅拷贝。而深拷贝不仅会复制对象,还会递归的复制对象所关联的对象,如果a深拷贝b,两者是完全相互独立等,但是深拷贝可能会遇到一个问题:一个对象如果直接或间接的引用了自身,会导致无休止的递归拷贝;所以用deepcopy函数来实现深拷贝操作的时候,其中deepcopy可以通过memo字典来保存已经拷贝过的对象,从而避免刚才所说的自引用递归问题;deepcopy函数的本质其实就是对象的一次序列化和一次返回序列化。
16、python多线程能用多个cpu么?python中多线程和多进程?
参考回答:在Cpython解释器中,多线程受制于GIL(全局解释器锁),在任意时刻中只允许单个python线程运行。无论系统有多少个CPU核心,python程序都只能在一个CPU上运行。在 CPython解释器中执行的每一个 Python线程,都会先锁住自己,以阻止别的线程执行,所以并不能利用CPU的多核特性,这是一个很大的问题。在CPython解释器中,全局解释锁GIL是在于执行Python程序的时候为了保护访问Python对象而阻止多个线程执行的一把互斥锁。由于GIL的存在,当线程被操作系统唤醒后,必须拿到GIL锁后才能执行代码,也就是说同一时刻永远只有一个线程在执行。
17、python的垃圾回收机制,(python是如何实现内存管理的?
参考回答:Python提供了自动化的内存管理,也叫垃圾回收机制,主要包括三个部分:引用计数、标记清理、分代回收。引用计数:对于Python解释器来说,Python中的每一个对象其实就是Object结构体,每个对象内部都维护了一个值,该值记录这此对象被引用的次数,当发生对象被创建,对象被引用,等这些操作会导致这个引用计数的值加1,当用del语句删除对象引用,对象引用被重新赋值其他对象等这些操作时,这个引用计数就会减1,如果次数变为0,它的内存就会被释放掉,Python垃圾回收机制会自动清除此对象。标记清理:“标记-清理”算法解决容器类型可能产生的循环引用问题。主要分为两个阶段:标记阶段,遍历所有的对象,如果对象是可达的(被其他对象引用),那么就标记该对象为可达;清除阶段,再次遍历对象,如果发现某个对象没有标记为可达,则就将其回收。分代回收:分代回收的基本思想是:对象存在的时间越长,是垃圾的可能性就越小,应该尽量不对这样的对象进行垃圾回收。Python中将对象分为三种世代分别记为0、1、2,每一个新生对象都在第0代中,如果该对象在一轮垃圾回收扫描中存活下来,那么它将被移到第1代中,存在于第1代的对象将较少的被垃圾回收扫描到;如果在对第1代进行垃圾回收扫描时,这个对象又存活下来,那么它将被移至第2代中,在那里它被垃圾回收扫描的次数将会更少。分代回收扫描的门限值可以通过gc模块的get_threshold函数来获得,该函数返回一个三元组,分别表示多少次内存分配操作后会执行0代垃圾回收,多少次0代垃圾回收后会执行1代垃圾回收,多少次1代垃圾回收后会执行2代垃圾回收。当代码中主动执行 gc.collect() 命令时,Python解释器就会进行垃圾回收。
18、python中的生成器是什么?说说你对生成器和迭代器、装饰器的理解,区别?
参考回答:首先我们要知道可迭代对象,简单的来理解就是可以使用 for 来循环遍历的对象。比如常见的 list、set和dict。可迭代对象具有__iter__ 方法,用于返回一个迭代器,因此,迭代器就是通过可迭代对象通过调用内建的 iter() 方法返回一个容器。生成器其实是一种特殊的迭代器,不过这种迭代器更加优雅。它不需要再像上面的类一样写__iter__()和__next__()方法了,只需要一个yiled关键字。函数装饰器可以在不修改原函数的条件下,为原函数添加额外的功能,本质就是一个函数,例如记录日志的运行性能,缓存等,我们定义好这个装饰器函数需要实现的功能,在python中通过一个@ 就可以在需要的装饰器的函数前直接调用。
19、Lambda函数是什么,举例说明的它的应用场景。
参考回答:Lambda函数也叫匿名函数,它是功能简单用一行代码就能实现的小型函数。Python中的Lambda函数只能写一个表达式,这个表达式的执行结果就是函数的返回值,不用写return关键字。Lambda函数因为没有名字,所以也不会跟其他函数发生命名冲突的问题。
20、python中为什么没有函数重载?
参考回答:C++、Java、C#等诸多编程语言都支持函数重载,所谓函数重载指的是在同一个作用域中有多个同名函数,它们拥有不同的参数列表(参数个数不同或参数类型不同或二者皆不同),可以相互区分。重载也是一种多态性,因为通常是在编译时通过参数的个数和类型来确定到底调用哪个重载函数,所以也被称为编译时多态性或者叫前绑定。这个问题的潜台词其实是问面试者是否有其他编程语言的经验,是否理解Python是动态类型语言,是否知道Python中函数的可变参数、关键字参数这些概念。首先Python是解释型语言,函数重载现象通常出现在编译型语言中。其次Python是动态类型语言,函数的参数没有类型约束,也就无法根据参数类型来区分重载。再者Python中函数的参数可以有默认值,可以使用可变参数和关键字参数,因此即便没有函数重载,也要可以让一个函数根据调用者传入的参数产生不同的行为。
21、写一个函数统计传入的列表中每个数字出现的次数并返回对应的字典。
参考回答:可以直接使用Python标准库中collections模块的Counter类来解决这个问题,Counter是dict的子类,它会将传入的序列中的每个元素作为键,元素出现的次数作为值来构造字典。
22、使用Python代码实现遍历一个文件夹的操作?
参考回答:Python标准库os模块的walk函数提供了遍历一个文件夹的功能,它返回一个生成器。os.path模块提供了很多进行路径操作的工具函数,在项目开发中也是经常会用到的。如果题目明确要求不能使用os.walk函数,那么可以使用os.listdir函数来获取指定目录下的文件和文件夹,然后再通过循环遍历用os.isdir函数判断哪些是文件夹,对于文件夹可以通过递归调用进行遍历,这样也可以实现遍历一个文件夹的操作。
23、__init__和__new__方法有什么区别?
参考回答:Python中调用构造器创建对象属于两阶段构造过程,首先执行__new__方法获得保存对象所需的内存空间,再通过__init__执行对内存空间数据的填充(对象属性的初始化)。__new__方法的返回值是创建好的Python对象(的引用),而__init__方法的第一个参数就是这个对象(的引用),所以在__init__中可以完成对对象的初始化操作。__new__是类方法,它的第一个参数是类,__init__是对象方法,它的第一个参数是对象。
24、函数参数*arg和**kwargs分别代表什么?
参考回答:Python中,函数的参数分为位置参数、可变参数、关键字参数、命名关键字参数。* args代表可变参数,可以接收0个或任意多个参数,当不确定调用者会传入多少个位置参数时,就可以使用可变参数,它会将传入的参数打包成一个元组。** kwargs代表关键字参数,可以接收用参数名=参数值的方式传入的参数,传入的参数的会打包成一个字典。定义函数时如果同时使用*args和**kwargs,那么函数可以接收任意参数。
25、说一下python中变量的作用域。
参考回答:Python中有四种作用域,分别是局部作用域(Local)、嵌套作用域(Embedded)、全局作用域(Global)、内置作用域(Built-in),搜索一个标识符时,会按照LEGB的顺序进行搜索,如果所有的作用域中都没有找到这个标识符,就会引发NameError异常。
26、说一下你对闭包的理解
参考回答:闭包是支持一等函数的编程语言(Python、JavaScript等)中实现词法绑定的一种技术。当捕捉闭包的时候,它的自由变量(在函数外部定义但在函数内部使用的变量)会在捕捉时被确定,这样即便脱离了捕捉时的上下文,它也能照常运行。简单的说,可以将闭包理解为能够读取其他函数内部变量的函数。正常情况下,函数的局部变量在函数调用结束之后就结束了生命周期,但是闭包使得局部变量的生命周期得到了延展。使用闭包的时候需要注意,闭包会使得函数中创建的对象不会被垃圾回收,可能会导致很大的内存开销,所以闭包一定不能滥用。
27、谈谈你对“猴子补丁”的理解。
参考回答:“猴子补丁”是动态类型语言的一个特性,代码运行时在不修改源代码的前提下改变代码中的方法、属性、函数等以达到热补丁(hot patch)的效果。很多系统的安全补丁也是通过猴子补丁的方式来实现的,但实际开发中应该避免对猴子补丁的使用,以免造成代码行为不一致的问题。在使用gevent库的时候,我们会在代码开头的地方执行gevent.monkey.patch_all(),这行代码的作用是把标准库中的socket模块给替换掉,这样我们在使用socket的时候,不用修改任何代码就可以实现对代码的协程化,达到提升性能的目的,这就是对猴子补丁的应用。
28、python中如何实现字符串替换操作?
参考回答:Python中实现字符串替换大致有两类方法:字符串的replace方法和正则表达式的sub方法。使用字符串的replace方法。
29、如何使用random模块生成随机数、实现随机乱序和随机抽样?
参考回答: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)函数可以从非空序列中取出一个随机元素。
30、举例说明什么情况下会出现KeyError、TypeError、ValueError。
参考回答:举一个简单的例子,变量a是一个字典,执行int(a['x'])这个操作就有可能引发上述三种类型的异常。如果字典中没有键x,会引发KeyError;如果键x对应的值不是str、float、int、bool以及bytes-like类型,在调用int函数构造int类型的对象时,会引发TypeError;如果a[x]是一个字符串或者字节串,而对应的内容又无法处理成int时,将引发ValueError。
31、如何读取大文件,例如内存只有4G,如何读取一个大小为8G的文件?
参考回答:很显然4G内存要一次性的加载大小为8G的文件是不现实的,遇到这种情况必须要考虑多次读取和分批次处理。在Python中读取文件可以先通过open函数获取文件对象,在读取文件时,可以通过read方法的size参数指定读取的大小,也可以通过seek方法的offset参数指定读取的位置,这样就可以控制单次读取数据的字节数和总字节数。在Linux系统上,可以通过split命令将大文件切割为小片,然后通过读取切割后的小文件对数据进行处理。外部排序跟上述的情况非常类似,由于处理的数据不能一次装入内存,只能放在读写较慢的外存储器(通常是硬盘)上。“排序-归并算法”就是一种常用的外部排序策略。在排序阶段,先读入能放在内存中的数据量,将其排序输出到一个临时文件,依此进行,将待排序数据组织为多个有序的临时文件,然后在归并阶段将这些临时文件组合为一个大的有序文件,这个大的有序文件就是排序的结果。
32、说一下你对python中模块和包的理解。
参考回答:每个Python文件就是一个模块,而保存这些文件的文件夹就是一个包,但是这个作为Python包的文件夹必须要有一个名为__init__.py的文件,否则无法导入这个包。通常一个文件夹下还可以有子文件夹,这也就意味着一个包下还可以有子包,子包中的__init__.py并不是必须的。模块和包解决了Python中命名冲突的问题,不同的包下可以有同名的模块,不同的模块下可以有同名的变量、函数或类。在Python中可以使用import或from ... import ...来导入包和模块,在导入的时候还可以使用as关键字对包、模块、类、函数、变量等进行别名,从而彻底解决编程中尤其是多人协作团队开发时的命名冲突问题。
33、正则表达式中的match和search方法?
参考回答:match方法是从字符串的起始位置进行正则表达式匹配,返回Match对象或None。search方法会扫描整个字符串来找寻匹配的模式,同样也是返回Match对象或None。
34、Python 中的 is 和 == 有什么区别?
参考回答:is 比较的是两个对象的id值是否相等,也就是比较两个对象是否为同一个实例对象,是否指向同一个内存地址。== 比较的是两个对象的内容是否相等,默认会调用对象的equal()方法。
35、当退出 Python 时是否释放所有内存分配?
参考回答:答案是否定的。那些具有对象循环引用或者全局命名空间引用的变量,在 Python 退出是往往不会被释放。另外不会释放 C 库保留的部分内容。
36、python Set的底层实现?
参考回答:散列表
37、__init__.py 文件的作用以及意义?
参考回答:这个文件定义了包的属性和方法,它可以什么也不定义;可以只是一个空文件,但是必须存在。如果 __init__.py 不存在,这个目录就仅仅是一个目录,而不是一个包,它就不能被导入或者包含其它的模块和嵌套包。或者可以这样理解。这样,当我们导入这个包的时候,__init__.py文件自动运行。帮我们导入了这么多个模块,我们就不需要将所有的import语句写在一个文件里了,也可以减少代码量。
#机器学习面经##python基础#林小白的机器学习指南,从本人面试的机器学习算法岗位出发,对机器学习“八股文”做详细的介绍、推导;