整理看到或者查到的面试题以及答案
目录
1 什么是Python?
Python是一种编程语言,它有对象、模块、线程、异常处理和自动内存管理,相对于其他语言python具有良好的跨平台性且容易学习,同时python是解释型语言,在代码运行前不需要解释,代码修改简单方便。但是python的执行速度不够快。
2 is和 == 的区别?
== 是比较字符,只是判断对象的值是否一致,而is则判断的是对象的身份(内存地址)是否一致。对象的身份可以通过id()方法来查看。
3 os和sys的区别?
os模块是Python标准库中提供的与操作系统交互的模块,提供了访问操作系统底层的接口,里面有很多操作系统的接口。
sys模块负责与python解释器的交互。
4 可变对象和不可变对象?
在python中,一切对象都包含三个属性:地址、类型、值
可变对象:
- 当对象的值发生变化,但内存地址没有发生变化时,则说明是可变对象。
- 可变对象:列表、字典、集合。
- 引用可变对象时,会创建新的内存地址,当可变对象的值发生变化时,原内存地址不会改变
- 引用传递:主函数向调用函数传递的参数是可变类型时,实际上是将实参的引用传入了调用函数,对引用的操作等于对其指定的对象进行操作。
不可变对象:
- 当对象的值发生变化时,但内存地址也发生改变时,则说明是不可变类型。
- 不可变对象:元组、字符串、数值
- 在引用不可变对象时,会寻找该对象是否被创建过,若该对象已经创建,则变量会直接引用该对象,不会在申请新的内存空间。
- 值传递:主函数向调用函数传递的参数是不可变的类型时,实际上只是将实参的拷贝(即临时副本)传递给了被调用函数,并不是实参本身,这样被调函数不能直接修改主函数中变量的值。
5 赋值、浅拷贝和深拷贝的区别?
赋值:
在python中,对象的赋值就是简单的对象调用。
a = [1 , 2, "hello", ["python", "C++"]
b = a
在上述的情况下,a和b是一样的,它们指向同一片内存,b只不过是a的别名,是引用。
赋值操作(包括对象作为参数、返回值)不会开辟新的空间,他只是复制了对象的引用。也就是说除了b这个名字外,没有其他内存的开销。修改了a,也就是影响了b,同理,修改了b也就影响了a。
浅拷贝:
浅拷贝是指重新分配一个内存空间,创建一个新的对象,但里面的元素是原对象中第一层对象的引用。
浅拷贝有操作:切片操作、工厂函数、copy.copy()函数。
b = a[:] or b = [i for i in a] # 切片操作
b = list(a) # 工厂函数
b = copy.copy(a) #copy函数
浅拷贝后列表a和b是不同的对象,修改列表b理论上不会影响到列表a。但是要注意的是,浅拷贝值拷贝了一层,在列表a中有一个嵌套的list,如果我们修改了它,情况就不一样了。
深拷贝:
深拷贝是指重新分配一块内存,创建一个新的对象,并对原对象中的元素,以递归的方式,通过创建新的子对象拷贝到新对象中。因此,新对象和原对象没有任何关联。
6 *args 和 **kwargs作用?
允许调用函数时,传入多个实参。
*args 会把位置参数转化为tuple(元组)
*kwargs 会把关键字参数转化为 dict(字典)
7 __new__ 和 __init__的区别?
__new__方法是真正的构造函数,是实例创建时被调用,它的任务是创建并返回该实例,是静态方法。
__init__方法是初始化方法,是实例创建之后被调用的,然后设置对象属性的一些初始值。
__new__方法在__init__方法之前被调用,并且__new__方法的返回值将传递给__init__方法作为第一个参数,最后__init__给这个实例设置一些参数。
8 什么是装饰器?
参考使用:
(3条消息) python装饰器这一篇就够了_凸头拯救地球的博客-CSDN博客_python装饰器
Python的装饰器本质上是一个嵌套函数,它接受被装饰的函数(func)作为参数,并返回一个包装过的函数。
作用:在不修改被装饰对象源代码以及调用方式的前提下为被装饰对象添加新功能。
装饰器模板:
import functools
def outer(origin):
@functools.wraps(origin)
def inner(*args, **kwargs):
# 这里写需要的功能
res = origin(*args, **kwargs):
return res
return inner
9 什么是迭代器和生成器?
可迭代对象:
python中一个非常强大的功能,它可以访问容器(字符串、列表、元组、集合、字典、range)
迭代是通过for循环遍历对象中的每一个元素,将元素取出来的过程,所以:容器都是可迭代的对象。
可迭代对象除了包含常见的序列,还包括一个叫迭代器的东西
迭代器:
- 迭代器是一个可以记住遍历的位置的对象
- 迭代器对象从集合的第一个元素开始访问,直到所有元素被访问完结束。迭代器只能往前不会后退
- 迭代器有两个基本方法:iter() 和next()。
- 字符串、列表或元组对象都可以用于创建迭代器。
生成器:
- 在python中,使用了yield的函数被称为生成器。
- 跟普通函数不同的是,生成器是一个返回迭代器的函数,只能用于迭代操作。
- 在调用生成器运行的过程中,每次遇到yield时函数会暂停并保存当前所有的运行信息,返回yield的值,并在下一次执行next()方法时从当前位置继续运行。
- 在调用生成器函数时,函数体并不执行,只返回一个生成器对象。
- 在对生成器对象使用next方法或者遍历的时候,生成器函数体才真正执行。
参考博客:
(3条消息) python中yield的用法详解——最简单,最清晰的解释_冯爽朗的博客-CSDN博客_python yield
10 filter 、map 和 reduce是什么?
filter函数:用于过滤序列,它接收一个函数和一个序列,把函数作用在序列的每个元素上,然后根据返回值是True还是False决定保留还是丢弃该元素。
mylist = list(range(10))
print(list(filter(lambda x: x % 2 == 1, mylist)))
[1, 3, 5, 7, 9]
map函数:传入一个函数和一个序列,并把函数作用到序列的每一个元素上,返回一个可迭代对象。
print(list(map(lambda x: x % 2, mylist)))
[0, 1, 0, 1, 0, 1, 0, 1, 0, 1]
reduce函数: 用于递归计算(列表累加求和),需要传入一个函数和一个序列,并把函数和序列元素的计算结果与下一个元素进行计算。
from functools import reduce
print(reduce(lambda x, y: x + y, range(101))
5050
11 什么是全局变量、局部变量?
在函数外声明的变量是全局变量。只在函数内起作用的变量叫做局部变量。全局变量可以在函数中直接进行访问,但是在修改全局变量时,为了避免与局部变量产生混淆,需要先加上global声明,然后在修改。
12 什么是闭包?
闭包:闭包是python编程一个非常重要的概念。如果一个外函数中定义了一个内涵数,且内函数引用到了体外的变量,这时外函数通过return 返回内函数的引用时,会把定义时涉及到的外部引用变量和内函数打包成一个整体(闭包)返回。
作用:即使外函数已经结束了,内函数依然能够使用外函数的临时变量。
闭包实际上就是一个函数,但是这个函数要具有:
- 定义在另外一个函数里面(嵌套函数);
- 引用其所在环境的自由变量。
def fun(a, b, c):
def para(x):
return a * x ** 2 + b * x + c
return para
f = fun(1, 2, 3)
print(f(2))
11
上面的函数中,f=fun(1,2,3)定义了一个一元二次函数的函数对象,如果要计算x= 2,只需要计算f(2)即可。
13 什么是匿名函数?
匿名函数的关键字是lambda,表现形式:lambda 参数:返回值,lambda后面的参数就是函数的形参,冒号后面的表达式就是返回值。
lambda表达式的意义两点:
- 对于只有一行的函数,使用此方式可以省去定义函数的过程,使代码简洁明朗
- 对于不需要重复使用之函数,此方式可以用完之后,立即释放,提高程序执行的性能。
虽然lambda函数可以接收任意多个的参数并返回当个表达式的值,但是lambda函数不能包含命令且包含的表达式不能超过一个。
14 python中类方法、类实例方法、静态方法有何区别?
参考:
Python实例方法、静态方法和类方法详解(包含区别和用法)
类方法:是类对象的方法,在定义的时候需要在上方使用"@classmethod"进行装饰,形参为cls,表示类对象,类对象和实例对象都可调用。
类实例方法:是类实例化对象的方法,只有实例对象可以调用,形参为self,指代对象本身。
静态方法:是一个任意函数,在其上方使用"@staticmethod"进行装饰,可以用对象直接调用,静态方法实际上跟该类没有太大关系。
15 什么是垃圾回收机制?
引用计数(主要手段)+标记清除(辅助)+分代回收(辅助)
1.引用计数
在Python中,使用了引用计数这一技术实现内存管理。一个对象被创建完成后就有一个变量指向这个对象,那么就这个对象的引用计数为1,以后如果有其他变量指向这个对象,其引用计数也会相应增加,如果将一个变量不再执行这个对象,那么这个对象的引用计数减1。如果一个对象没有任何变量指向这个对象,也即引用计数为0,那么这个对象会被Python回收。
优点:
- 高效。
- 运行期没有停顿。可以类比一下Ruby的垃圾回收机制,也就是实时性:一旦没有引用,内存就直接释放了。不用像其他机制等到特定时机。实时性还带来一个好处:处理回收内存的时间分摊到了平时。
- 对象有确定的生命周期。
- 易于实现。
缺点:
- 维护引用计数消耗资源,维护引用计数的次数和引用赋值成正比。
- 无法解决循环引用的问题。比如现在有两个对象分别为
a
和b
,a
指向了b
,b
又指向了a
,那么他们两的引用计数永远都不会为0。也即永远得不到回收。
2.标记清除
针对循环引用的情况,Python引入标记清除算法。
标记清除算法是一种基于追踪回收技术实现的垃圾回收算法。它分为两个阶段:
- 第一阶段是标记阶段,GC会把所有的活动对象打上标记
- 第二阶段是把那些没有标记的对象非活动对象进行回收。
3.分代回收
分代回收是建立在标记清除技术基础之上的,是一种以空间换时间的操作方式。
Python将内存根据对象的存活时间划分为不同的集合,每个集合称为一“代”, Python将内存分为了3“代”,分别为年轻代(第0代)、中年代(第1代)、老年代(第2代),他们对应的是3个链表,它们的垃圾收集频率与对象的存活时间的增大而减小。新创建的对象都会分配在年轻代,年轻代链表的总数达到上限时,Python垃圾回收机制就会被触发,把那些可以被回收的对象回收掉,而那些不会回收的对象就会被移到中年代去,依此类推,老年代中的对象是存活时间最久的对象,甚至是存活于整个系统的生命周期内。
参考: