
郑大钱呀
2023/03/18阅读:16主题:凝夜紫
Python 面试题,参考一下
关注我啦
关注【郑大钱呀
】【~公~】【~众~】【~号~】,我们一起交流,一起学习。
问题1
「问:」 ==和is的区别是? 「答:」
-
is
用于判断两个变量引用对象是否为同一个,即判断对象的内存地址,可以通过id()
函数,能够获取对象的内存地址 -
==
用于判断引用变量的值是否相等
「代码演示和讲解」
a=[1,2,3]
b=a[:]
print(a==b,a is b)
# 运行结果如下:True False
上面的a和b两个列表的内容是一样的,但是内存地址不一样,肯定有人疑问,为什么举的例子,这么特殊,像这样a=1;b=1;
这样不行么?我们看一段示例代码:
a=1
b=1
print(a==b,a is b)
# 运行结果如下:True True
通过上面的代码,发现两者的结果一致,这是因为,Python出于对性能的考虑,凡是不可变对象
,在同一个代码块中的对象,只要是值相同的对象,就不会重复创建,而是直接引用已经存在的对象。类似的问题:!=和is not运算符的区别
原理都是一样的
问题2
「问:」 什么是lambda函数? 「答:」 Lambda函数是不带名称的单行函数
,也被称为匿名函数
,它直接接受参数的数量以及使用该参数执行的条件或操作,该参数以冒号分隔,并返回最终结果。lambda函数有如下特点:
-
没有名称 -
Lambda函数有返回值,但是不能写 return
-
函数只能写在一行中 -
不用于代码重用
Lambda函数适用于高阶函数(将一个函数的函数名作为参数传给另外一个函数,被传参的函数就称为高阶函数),举个例子,如果我们想用一个函数,实现加减两种计算,我们就可以这样来实现。
「代码演示和讲解」
def calc(func,a,b):
return func(a,b)
add_func = lambda a,b: a+b
sub_func = lambda a,b: a-b
add_res=calc(add_func,1,2)
sub_res=calc(sub_func,1,2)
print(add_res,sub_res)
# 运行结果如下:3 -1
问题3
「问:」 Python中的生成器是什么?
「答:」 生成器简单的说就是按照一定的规则逐个
的生成数据的代码,生成器生成一个数据后,yield关键字就会把数据返回出去,并将函数处于阻塞状态,直到下一次使用next()
方法将其唤醒,再输入下一个值。
生成器与普通函数的区别就是:假如我们要喝10000瓶果汁,普通函数是一次性把10000瓶果汁生产好,然后放到仓库里,你去喝,而生成器是你要喝的时候我生产一瓶给你,喝完了想再喝了,就告诉我,我在给你生产,这样的好处,不需要一个很大的仓库,这里的仓库就是我们电脑的内存。
「代码演示和讲解」
def generator_test():
for i in range(0,100):
yield i;
g = generator_test()
print(next(g),next(g),next(g),next(g))
# 运行结果 0 1 2 3
上面的代码是生成0-99 一百个数,但是由于我们这里使用了生成器,并没有一次性生成,而是当我们需要的时候,使用next()方法唤醒取出下一个数。
问题4
「问:」 如何使用索引来反转Python中的列表元素?
「答:」
a=[1,2,3,4,5]
print(a[::-1])
# 运行结果 [5, 4, 3, 2, 1]
问题5
「问:」 描述一下break语句的作用?
「答:」
-
break语句用来终止循环语句,即循环条件没有False条件或者语句还没有执行全部,也会停止执行循环语句。 -
break语句用在while和for循环中。如果 使用嵌套循环,break语句将停止执行最深层的循环
,并开始执行下一行代码,示例代码如下
「代码演示和讲解」
for i in range(0,100):
for j in range(0,2):
if i==10:
print("break",i)
break;
if i%25==0:
print(i)
"""
运行结果如下:
0
break 10
25
50
75
"""
问题6
「问:」 解释下python的GIL?
「答:」 Python解释器的实现是有多个版本的:CPython(CPython就是用C语言实现Python解释器), Jpython等等,而python 默认的官方解释器为Python,GIL的问题实际上是存在于CPython中的。
GIL 是python的全局解释器锁,同一进程中假如有多个线程运行,线程非独立的,同一进程里的线程的数据是共享的,当各个线程访问数据资源时会出现竞争
状态,造成数据混乱,这就是线程的不安全。所以引进了互斥锁,确保某段关键代码、共享数据只能由一个线程从头到尾完整地执行。保证同一时刻只有一个线程在解释器中运行呢
所以Python语言中多线程其实是假的多线程,
即多个线程的代码,轮流被解释器执行,只不过切换的很频繁很快,给人一种多线程同时在执行的错觉,其实就是并发(不同的代码块交替执行)
,而不是并行(不同的代码块同时执行)
解决GIL问题的方法是,使用多进程,用multiprocess(多进程)替代Thread,每个进程有自己的独立的GIL,因此也不会出现进程之间的GIL争抢,但是多进程会带来更大的内存开销,也会增加程序实现时线程间数据通讯和同步的困难。
问题7
「问:」 python实现列表去重的方法?
「答:」
list = [1,234,44,34,5,1,4,5,5]
print(set(list))
# 运行结果如下:{1, 34, 4, 5, 234, 44}
问题8
「问:」 函数中的*args,**kwargs参数什么意思?
「答:」 *args 和**kwargs,为不定长参数,主要用于函数定义。你可以将不定数量的参数传递给一个函数。这里的不定的意思是:预先并不知道,函数使用者会传递多少个参数给你,所以在这个场景下使用这两个关键字。
「代码演示和讲解」
def func_test_list(*args):
print("args",args);
def func_test_dict(**kwargs):
print("kwargs",kwargs);
func_test_list(1,23,3,4)
func_test_dict(name="test",sex="male")
# 运行结果如下
#args (1, 23, 3, 4)
#kwargs {'name': 'test', 'sex': 'male'}
*args
使用用来接受非键值对的不定长参数,而**kwargs
是用来接受键值对的不定长参数。
问题9
「问:」 介绍一下Python中range()函数?
「答:」 range的作用,是在指定的范围,根据步长,生成返回一个可迭代对象,并不是迭代器,也是可惰性迭代的一种,即不管range对象表示的整数序列有多长,所有range对象占用的内存空间都是相同的,因为仅仅需要储存start,stop,step,只有当用到range对象时,才会去计算序列中的相关元素,,值得注意的是:可迭代对象不支持next()方法,而迭代器本身不支持切片方法
「代码演示和讲解」
a = range(1,1000)
print(type(a)
# 运行结果如下
#<class 'range'>
问题10
「问:」 简述Python的super类
「答:」 首先super不是方法不是函数也不是关键字啥的,是个类,作用是调用父类的某个方法,一般多用于继承中,比如有两个类,父类A,子类B,子类继承父类,此时我们会在子类中写这一句super(B,self).__init__()
,可以这样理解:super(B, self)首先找到B的父类(就是类A),然后把类B的对象self转换为类A的对象,然后“被转换”的类A对象调用自己的__init__函数。
「代码演示和讲解」
class A():
def __init__(self,msg):
print("__init__ A")
self.msg =msg
def say(self):
print("say",self.msg)
class B(A):
def __init__(self):
print("__init__ B")
if __name__ == '__main__':
b = B()
b.say()
运行报错如下:
__init__ B
Traceback (most recent call last):
File "Study.py", line 16, in <module>
b.say()
File "Study.py", line 7, in say
print("say",self.msg)
AttributeError: 'B' object has no attribute 'msg'
上面B类继承了父类,但是没有调用父类的初始化方法,所以在调用父类的方法的时候就报错了,这个时候我们用super关键字,调用父类的构造方法初始化一下,代码如下:
class A():
def __init__(self,msg):
print("__init__ A")
self.msg =msg
def say(self):
print("say",self.msg)
class B(A):
def __init__(self,name):
#写法一:super(当前类名,self).__init__(父类初始化需要的参数)
# super(B,self).__init__(name)
#写法二:super().__init__(父类初始化需要的参数)
super().__init__(name)
print("__init__ B")
if __name__ == '__main__':
b = B("Mike")
b.say()
运行结果如下:
__init__ A
__init__ B
say Mike
super(当前类名,self)
,第一个是type(也就是一个class),第二个是type或者ojbect,第二个参数决定了要把父类的初始化方法绑定到哪个对象身上。
问题11
「问:」 简述面向对象中__new__方法
「答:」
-
__new__是一种负责创建类实例的静态方法,它无需使用 staticmethod 装饰器
修饰,且该方法会优先__init__()
初始化方法被调用,覆写__new__方法需要调用超类的super().「new」()。 -
__new__至少要有一个参数cls,代表当前类,此参数在实例化时由Python解释器自动识别 -
_new__必须要有返回值,返回实例化出来的实例
「代码演示和讲解」
class Study:
def __new__(cls,*args,**kwargs):
print("__new__():",cls,args,kwargs)
instance = super().__new__(cls)
return instance
def __init__(self):
print("__init__")
Study.__new__(Study,[1,2,3,4],name="test")
运行结果如下:
__new__(): <class '__main__.Study'> ([1, 2, 3, 4],) {'name': 'test'}
一般情况下,覆写 「new」() 的实现将会使用合适的参数调用其超类的 super().「new」(),并在返回之前修改实例
问题12
「问:」 Python 实现冒泡排序
「答:」
冒泡排序(Bubble Sort)是一种简单直观的排序算法,它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成,这个算法的名字由来是因为越小的元素会经由交换慢慢"浮"到数列的顶端。
「代码演示和讲解」
def sortList(args):
for i in range(len(args)-1):
for j in range(len(args)-i-1):
if args[j]>args[j+1]:
# 前一个数比后一个数大,则互换位置
args[j],args[j+1]= args[j+1],args[j]
print("第%s轮排序完结果 %s" %((i+1),str(args)))
return args
sortList([5,4,3,2,1])
运行结果如下:
第1轮排序完结果 [4, 3, 2, 1, 5]
第2轮排序完结果 [3, 2, 1, 4, 5]
第3轮排序完结果 [2, 1, 3, 4, 5]
第4轮排序完结果 [1, 2, 3, 4, 5]
我们以五个数,要从从小到达排序为例,五个数相邻两个数比较只需比较四次,即可找出本轮比较的最大值,经过第一轮比较已经找出最大值,所以第二轮只需要比较前四个数,在这四个数中比较,找出这个四个数中的最大值,也就是所有数中的第二大值,以此类推,五个数,只需经过4轮比较即可对列表完成全部排序
参考文档
结尾
为了方便大家学习交流,我们建立了一个交流群,以技术(以软件测试为主)和业务(以金融业务为主)学习交流为主,如有需要加入,可以扫描文章末尾的二维码入群。
本期的内容就到这里了,如有不足之处还请大家多多指正,欢迎大家
留言
、关注
、转发
、收藏
,谢谢。

作者介绍

郑大钱呀
微信搜索【郑大钱呀】公众号