admin管理员组文章数量:1544024
字节跳动面经
字节跳动游戏面经(一、二、三+boss(hr)面)
内推链接
面试+总结
总结
机器学习
Python基础
C++
计算机网络
操作系统
计算机组成
数据库
sql语句
Linux命令
git
数据结构
字符串
数组
链表
栈
二叉树
矩阵
算法
测试实例
软件、产品知识
逻辑思维
机器学习
KNN 和 K-Means 算法区别Python语言基础知识
1、深拷贝和浅拷贝
直接复制:所有元素都是地址拷贝
浅拷贝:可变对象是值拷贝,不可变对象是地址拷贝
深拷贝:所有元素都是值拷贝
可变对象,list,dict,set
不可变对象,tuple,int,float,str,long 等数据类型
a = [1,2,3,4,[5,6]]
b = a
# list自带拷贝,浅拷贝
c = a.copy()
# 浅拷贝
d = copy.copy(a)
#深拷贝
e = copy.deepcopy(a)
a.append(7)
a[4].append(8)
a[2] = 10
? 手写深拷贝
使用isinstance()进行类型判断
判断是否是可变类型,list,dict,set({ }开头 )
或是可包含多种其他类型的 tuple
def deepcopy(cls):
if isinstance(cls,list):
li =[]
for item in cls:
# 可变数据结构中的每一个元素,进行递归调用
li.append(deepcopy(item))
return li
elif isinstance(cls,dict):
dic = {}
for k,v in cls.items():
dic[k] = deepcopy(v)
return dic
elif isinstance(cls,tuple):
li = []
for item in cls:
li.append(deepcopy(item))
return li
else:
return cls
res = [1,4,3,[3,2,4],{1:'d',3:'t'},('j','a')]
ans = deepcopy(res)
print(ans)
2、python多线程
为什么python多线程不是真正意义上的多线程
GIL 全局解释器锁保证每次只有一个线程运行,虽然是4核,但是同一时间仍只能运行一个线程,不过是使用轮转
from threading import Thread
def loop():
while(True):
pass
# 创建三个线程
for i in range(3):
t = Thread(target = loop)
t.start()
while(True):
pass
python 多进程可以利用多核
#coding=utf-8
from multiprocessing import Pool
from threading import Thread
from multiprocessing import Process
def loop():
while True:
pass
if __name__ == '__main__':
for i in range(3):
t = Process(target=loop)
t.start()
while True:
pass
python不适合计算密集型任务,可以使用多进程实现多核任务,多个python拥有各自的GIL锁,互不影响
3、垃圾回收机制 参考
-
引用计数 (reference counting)主要
每个对象都有一个 ob_refcnt 最为引用计数。
有新引用时,加1。
引用它的对象被删除后,计数减1
计数为0时,对象被回收sys.getrefcount(a)查看对象引用计数
计数+1:
创建、引用、作为参数
计数-1:
别名销毁 def a
别名赋予新对象: a = 24
离开作用域python对-5~256建立了整数常量池
优缺点
优点:简单、高效
缺点:资源消耗大,循环引用问题(不在运行对象,相互调用,计数永远都是为1,垃圾回收无法进行回收) -
标记清除(mark - Sweep)
为解决计数引用中的循环引用问题
对象为节点,引用为边构成有向图
- 标记阶段
从根对象(全局变量、调用栈)出发,遍历所有对象,可以到达的对象进行标记 - 回收阶段
遍历结束,未到达对象为非活动对象,会被回收
- 分代回收
建立了三个代,代表不同的存活时间,分为年轻代(第0代),中年代(第二代),老年代(第三代),对应3个链表,越年轻的代代表越容易被回收。
当年轻代链表总数达到上限,垃圾回收出发,未被回收的对象就转到中年代。以此类推
建立在标记清除基础上
4、list、tuple、set、dict底层结构 参考1参考2
-
list 列表(可变长的数组)
顺序表或链表实现 (动态顺序表,单链表、双链表)
对其他对象的引用组成的连续数组 -
tuple 元组
与列表类似,但元组不可变,一旦创建不能修改
大数量集时,tuple和list速度差不多
小数量集时,tuple会有对象池 -
dict 字典
数据结构:散列表(哈希表)
键:对
只有可哈希对象才能作为键(不可变对象)
可变对象:列表、字典、集合建立哈希表过程:
1)数据添加
将key利用哈希函数转化为整数数字,用整数数字对数组长度进行取余,结果作为下标。
若该结果已有数据,产生哈希冲突,使用哈希冲突解决2) 数据查询
用哈希函数将key转化为数组下标,定位查询 -
set 集合
数据结构:散列表
去除重复的值
没有键的引用,只有值的引用。其他和字典相同去重实现:
- __hash__函数 :判断两个变量哈希值是否相同
- __eq__方法:两函数哈希值相同,判断变量是否是同一个,True去重,False不去重
5、面向对象的特征 参考
-
封装
对全局作用域隐藏内部实现
__开头变为私有变量,只有内部可以访问
**XXX**为特殊变量,可以访问 -
继承
父类、子类
子类可以继承父类的属性(私有属性不会)、重写父类方法、重写object方法
继承属性
#父类
class Person():
def __init__(self,name=None,age=None,sex=None):
self.name=name
self.age=age
self.sex=sex
def eat(self):
print("%s正在吃饭"%self.name)
#学生子类:继承人类父类的属性
class Student(Person):
#子类的初始化参数要和父类的一样,否则没有办法给父类传参,会报错
def __init__(self,name=None,age=None,sex=None,score=None):
# self.name=name
# self.age=age
# self.sex=sex
#上面三行的代码等价于下面一行的代码,都是给父类的属性传参
Person.__init__(self,name,age,sex)
#还可以这样写
#super().__init__(name,age,sex)
self.score=score
#这个可以是子类独有的方法,不会影响到父类
def study(self):
self.eat()
print("%s在学习,考了%d分"%(self.name,self.score))
#实例化一个学生对象,然后可以调用子类的方法,也可以直接调用父类的方法
stu=Student("汤姆",20,"男",80)
stu.study()
以上代码运行结果为:
汤姆正在吃饭
汤姆在学习,考了80分
子类对父类方法重写
对父类方法,object方法都可以重写
#父类
class Person():
def __init__(self,name=None,age=None,sex=None):
self.name=name
self.age=age
self.sex=sex
def eat(self):
print("%s正在吃饭"%self.name)
#学生子类:继承人类父类的属性
class Student(Person):
def __init__(self,name=None,age=None,sex=None,score=None):
# self.name=name
# self.age=age
# self.sex=sex
#Person.__init__(self,name,age,sex)
super().__init__(name,age,sex)
self.score=score
def study(self):
self.eat()
print("%s在学习,考了%d分"%(self.name,self.score))
#方法的重写,调用的时候调用子类的方法
#可以对自定义的方法进行重写
def eat(self):
print("%d的%s正在吃饭,他是%s的"%(self.age,self.name,self.sex))
#也可以对自带的object类的方法进行重写。
def __str__(self):
return "姓名:{0},年龄:{1},性别:{2},成绩:{3}".format(self.name,self.age,self.sex,self.score)
def __lt__(self,other):
""" if isinstance(other,Student):
return self.age<other.age
else:
return False """
if self.name==other.name:
return self.age<other.age
else:
return self.name<other.name
#实例化
stu=Student("汤姆",20,"男",80)
stu.study()
stu.eat()
list1=[]
stu1=Student("杰克",20,"男",90)
stu2=Student("杰森",21,"男",20)
stu3=Student("杰森",12,"女",50)
list1.append(stu)
list1.append(stu1)
list1.append(stu2)
list1.append(stu3)
for student in list1:
print(student)
list1.sort()
for student in list1:
print(student)
以上代码输出为:
20的汤姆正在吃饭,他是男的
汤姆在学习,考了80分
20的汤姆正在吃饭,他是男的
姓名:汤姆,年龄:20,性别:男,成绩:80
姓名:杰克,年龄:20,性别:男,成绩:90
姓名:杰森,年龄:21,性别:男,成绩:20
姓名:杰森,年龄:12,性别:女,成绩:50
姓名:杰克,年龄:20,性别:男,成绩:90
姓名:杰森,年龄:12,性别:女,成绩:50
姓名:杰森,年龄:21,性别:男,成绩:20
姓名:汤姆,年龄:20,性别:男,成绩:80
如上,我们对自定义的eat()方法进行了重写,也对
多继承
一个子类继承多个父类
当继承多个父类时,默认调用前面一个父类
- 多态
子类,父类存在同样方法,子类会覆盖父类的方法
运行时,会调用子类方法
简单工厂模式:
用户输入选择汉堡,但是不知道内部如何实现,只要得到一个汉堡的实例对象
不同类型汉堡都有相同的make()方法,但是选择不同的子类,制作不同
工厂类 --> 汉堡类 --> 汉堡子类 --> make()制作
不需要为不同汉堡设计不同make方法
#创建汉堡的父类,并根据父类创建几个子类
class Hamburger:
def make(self):
print("您没有正确选择要制作的汉堡,请重新输入")
class FishHamburger(Hamburger):
def make(self):
print("您的鱼肉汉堡已经制作好了")
class BeafHamburger(Hamburger):
def make(self):
print("您的牛肉汉堡已经制作好了")
class ChickenHamburger(Hamburger):
def make(self):
print("您的鸡肉汉堡已经制作好了")
#工厂类,用来判断用户输入的值并创建相应的对象
class HamburgerFactory:
@classmethod
def getinput(cls,temp):
if temp=="1":
ch=FishHamburger()
elif temp=="2":
ch=BeafHamburger()
elif temp=="3":
ch=ChickenHamburger()
else:
ch=Hamburger()
return ch
#主方法,通过用户输入的值调用工厂的类方法
while True:
temp=input("请输入您要制作汉堡的序号,1.鱼肉汉堡,2.牛肉汉堡,3.鸡肉汉堡")
if temp=="1" or temp=="2" or temp=="3":
ch=HamburgerFactory.getinput(temp)
ch.make()
break
else:
ch=Hamburger()
ch.make()
continue
6、a == b a is b
a == b 比较运算法,判断两对象 值是否相同
a is b 判断两对象物理地址是否相同
a is b 返回True
- 小整数对象 [-5,256],相同数值的对象属于同意对象
- 相同简单字符串,也属于同意对象
- 直接复制
其他情况,a is b 返回False
7、多态的理解
问题 5
8、if(a1 && a2 && a==3) 什么情况下返回true
当 a 变量是一个 使用后自增的变量
9、内存模型、异常处理
不可变数据类型:一个固定区域存储。若数值发生变化,将内存会变化
可变数据类型:存储的数据的地址,真实存储区域大小是可以变化的
引用计数、标记清除、分代回收
10、面向对象特性
问题 5 面向对象特性
11、全局变量,局部变量 变量名是否可以一样
局部变量名和全局变量名 可以一样
- 函数内可以使用 全局变量,但是无法直接修改(可变类型除外)
- 函数内可以定义和全局变量相同的局部变量,他们作用域不同
- 函数内想要修改全局变量,使用global关键字
12、GIL
问题 3 python垃圾回收机制
13、python多线程适合CPU密集型程序吗
问题 2 python 多线程
不适合,GIL限制一次只能有一个线程运行,不是真的多线程
14、python装饰器 参考
函数装饰器:封装一个函数
python函数可以像普通变量一样传递
装饰器 本质是python函数或类,使其他函数/类在不用修改情况下,增加额外功能
集
应用场景:插入日志、性能测试、事务处理、缓存、权限校验等场景
15、lambda函数 参考
python两种函数:def、lambda(匿名函数)
lambda argument_list : expression
argument_list : 参数列表
expression : 参数表达式
如:
map(lambda x : x**x, [y for y in range(10)])
lambda函数是程序更紧凑,但是只能有一条表达式组成
16、dict的key可以是list吗,为什么
不可以
dict的底层实现原理
可哈希的数据类型才可以当作键
可哈希:tuple、str、objects对象集
不可哈希:list、set、dict
set中的元素是可哈希的
17、哈希冲突怎么办,冲突点放单链表还是双链表
冲突点放单链表,双链表都是可以的。
但是若是删除操作频繁,可以选择双链表。删除效率为 1
删除时,是知道元素的具体地址的 ,双链表知道一个元素的地址后,可以快速的将该值删除
而单链表,只能从头开始遍历,找到删除节点的前驱节点,再删除 删除效率为 n
哈希表原理:
-
哈希函数(散列函数):将键值通过哈希函数映射到一个0~M-1的索引中 (M为哈希表的长度)
余数法(整数)
乘数法(浮点数) -
哈希冲突处理:多个键值,对应相同的哈希函数值
开放地址法- 线性探测法:
fi = f(key + i ) % M i = 0 ~ M-1
插入:若哈希函数值插入位置有元素存在,则插入该位置后续中第一个空余位置
冲突解决:计算哈希函数,查看该位置键值是否等于查找键值,不相同查询后一个元素 - 二次探测法
fi = f(key + di ) % M di = 1^2 -1^2 2^2 -2^2 … - 伪随机探测法
fi = f(key + m) % M m 为随机数
链表法
哈希表中的每个索引都对应着一个链表,链表中所有元素都是哈希值为索引的元素插入:哈希数组中的每个
- 线性探测法:
本文标签: 测试
版权声明:本文标题:测试面试准备 内容由热心网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:https://m.elefans.com/xitong/1727102916a1097944.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论