Python知识点
1.解释器锁(GIL)
一个进程在运行时假如有多个线程,同一时间只能有一个线程处于运行状态,霸占python解释器(即加了一把全局解释器锁,GIL),当遇到耗时操作,会释放GIL运行其他线程。所以多线程中线程的运行也是有先后顺序,并不是同时进行。多进程中每个进程都会有各自的一把锁,可以实现多个进程同时运行,程序的运行效率会加快,缺点是系统开销大。
2.内建数据类型
- 数字类型(包括整数型和浮点类型)
- 字符串
- 布尔值
- 列表(list)
- 元组(tuple)
- 字典(dict)
- 集合(set)
3.数据结构
- 列表(list)
- 元组(tuple)
- 字典(dict)
- 集合(set)
4.__new__和__init__方法的区别
- __init__是构建函数,比较常用;__new__是实例化方法,不太常用
- __init__实例级别的函数,__new__类级别的函数
- __new__是创建一个新的实例,__init__构建一些实例的属性,__new__在__init__之前执行
- __new__必须要有一个参数(cls)代表当前类,且必须有返回值,表示生成的实例,可以是父类__new__出来的实例,也可以是Object__new__出来的实例;__init__必须要有一个参数(self)表示当前实例,不需要有返回值
5.with方法打开文件做了什么
如果按照正常的f.open()方法打开文件可能会出现一些异常,此时要写try、except、finally做异常判断,且无论如何都要在finally中关闭文件,即f.close(),with帮我们实现了finally中的f.close()
6.列表[1,2,3,4,5],请使用map()函数输出[1,4,9,16,25],并使用列表推导式提取出大于10的数,最终输出[16,25]
def test(num): # 幂运算 return num ** 2 l = [1,2,3,4,5] m = map(test,l) l2 = [i for i in m if i > 10] print(l2)
7.python中生成随机整数、随机小数、0—1之间小数方法
import random # 生成0-1之前的随机小数 print(random.random()) # 生成指定范围内的随机整数 print(random.randint(0,100)) # 生成随机小数 # 导入numpy库 import numpy # 生成5个随机小数 print(numpy.random.randn(5))
8.<div class="nam">中国</div>,用正则匹配出标签里面的内容(“中国”),其中class的类名是不确定的
import re str = '<div class="nam">中国</div>' t = re.findall(r'<div class=".*">(.*?)</div>',str) print(t)
9.python2和python3区别?列举5个
- python2中print函数可以用小括号包括要打印的内容,也可以用空格隔开;python3只能用小括号
- python2中unicode表示字符串序列,str表示字节序列;python3中str表示字符串序列,byte表示字节序列
- python中用raw_input()函数,python3使用input()
- python2为正常显示中文,需要引入coding;python3不需要
- python2使用ascii编码;python3使用utf-8编码
- python2中ranage(1,10)返回是一个数字列表;python3返回是一个迭代器,节约内存
10.列出python中可变数据类型和不可变数据类型,并简述原理
可变对象:列表list、字典dict
对这些数据类型进行操作时,只是修改变量的值,变量在内存中引用的地址还是同一个不会改变。
不可变对象:数值、字符串、元组tuple
相同值的对象在内存中只有一个地址,修改值即新建了一个对象,变量在内存中引用的地址会改变。
11. s = “ajldjlajfdljfddd”,去重并从小到大排序输出”adfjl”
s = "ajldjlajfdljfddd" se = set(s) l = [] for i in se: l.append(i) l.sort() # 这里注意:因为字符串是不可变数据类型, # 所以要先转换成列表排序,最后拼接成字符串 str1 = '' for i in l: str1 += i print(str1)
12.字典根据键从小到大排序
d={"name":"zs","age":18,"city":"深圳","tel":"1362626627"}
d1 = {i:d[i] for i in sorted(d)}
print(d1)
13.利用collections库的Counter方法统计字符串每个单词出现的次数”kjalfj;ldsjafl;hdsllfdhg;lahfbl;hl;ahlf;h”
from collections import Counter str1 = 'kjalfj;ldsjafl;hdsllfdhg;lahfbl;hl;ahlf;h' c = Counter(str1) print(c)
14.字符串a = “not 404 found 张三 99 深圳”,每个词中间是空格,用正则过滤掉英文和数字,最终输出”张三 深圳”
import re a = "not 404 found 张三 99 深圳" l = a.split(' ') num_str = re.findall(r'\d+|[a-z]+',a) for i in num_str: if i in l: l.remove(i) new_str = "".join(l) print(new_str)
15.正则re.complie作用
把正则表达式编译成一个对象,方便重复使用。
16.数据库优化查询方法
# 定义异常类,继承Exception基类 class MyException(Exception): # def __init__(self,*args,**kwargs): # self.args = args # self.kwargs = kwargs pass def test(num1,num2): try: total = num1 * num2 if total % 2 == 0: raise MyException('我自闭了!') else: print(total) except Exception as e: print(e) test(5,3)
19.正则表达式匹配中,(.)和(.?)匹配区别?
(.*):贪婪匹配,会把符合正则的尽可能多的往后匹配
(.*?):非贪婪匹配,尽可能少匹配
20.简述Django的orm
ORM,意为对象-关系映射,只需要简单的配置就可以变更数据库,不需要修改代码,只需要面向对象编程;ORM可以根据数据库引擎,翻译出对应的SQL语句,所以使用Django框架的项目可以不用关心底层使用的是什么数据库,如果数据库迁移,只用变更数据库引擎即可。
21.举例说明zip()函数用法
# 把两个可迭代的内容组合在一起生成一个tuple类型组成的内容 l1 = ['a','b','c'] l2 = [1,2,3] for i in zip(l1,l2): print(i)
22.提高python运行效率的方法
- 使用生成器,可以节约大量内存
- 多线程、多进程、协程
- 循环代码优化,避免过多重复代码执行
- 核心代码使用Cpython、PyPy等
- 多个if,elif条件判断,可以把最有可能发生的写在前面,可以减少程序判断的次数,提高效率
23.简述mysql和redis区别
Mysql:关系型数据库,数据存储在硬盘中,读写会有IO操作,速度相对较慢
Redis:内存型非关系数据库,数据保存在内存中,读写速度快
24.遇到bug如何处理
- 首先根据报错信息,定位大概出错的地方
- 一般细节上的错误,用print()打印分段检测程序的问题,如果能正常执行到print(),说明上面代码没问题;js可以用alert()或者console.log()
- 对于BUG的管理和归类总结,一般测试人员将测试出来的BUG提交到BUG管理工具上,我会根据提交的BUG一条一条的修改,修改的过程也是理解项目业务逻辑和提高编程逻辑缜密性的方法,最后我会对修改过的BUG做收藏和笔记记录
25.正则匹配,匹配日期2018-03-20
url=’https://sycm.taobao.com/bda/tradinganaly/overview/get_summary.json?dateRange=2018-03-20%7C2018-03-20&dateType=recent1&device=1&token=ff25b109b&_=1521595613462‘
url='https://sycm.taobao.com/bda/tradinganaly/overview/get_summary.json?dateRange=2018-03-20%7C2018-03-20&dateType=recent1&device=1&token=ff25b109b&_=1521595613462' import re re_com = re.compile(r'dateRange=(.*?)%7C(.*?)&') find_str = re_com.findall(url) print(find_str)
26.list=[2,3,5,4,9,6],从小到大排序,不许用sort,输出[2,3,4,5,6,9]
# 思路:获取列表中的最小值添加到新列表,然后删除老列表中最小元素,重复操作 l=[2,3,5,4,9,6] l2 = [] def get_min(l): min_value = min(l) l2.append(min_value) l.remove(min_value) for i in range(len(l)): get_min(l) print(l2)
27.写一个单例模式
单例模式:简单来说就是无论实例化多少次,都只有一个对象;比如,ORM模型关系映射时,只需要生成一个对象和数据库对接
class single(): # 声明一个类变量用来存放生成对象方法的引用 # 如果该值为空,说明没有生成过对象;反之,直接返回该对象的引用 _instance = None def __new__(cls,name,age): if not cls._instance: cls._instance = object.__new__(cls) return cls._instance s1 = single('张三',18) s2 = single('李四',19) s1.sex = 1 print(s1.sex) print(s2.sex) print(id(s1)) print(id(s2))
28.保留两位小数,题目本身只有a=”%.03f”%1.3335,让计算a的结果,为了扩充保留小数的思路,提供round方法(数值,保留位数)
a="%.03f"%1.3335 print(a,type(a)) print(float(a),type(float(a))) print(round(float(a),2))
29.列出常见的状态码和意义
- 200 OK:请求成功
- 400 Bad Request:客户端请求有语法错误,不能被服务器所理解
- 401 Unauthorized:请求未经授权
- 403 Forbidden:服务端收到请求,但拒绝提供服务
- 404 Not Found:请求资源不存在
- 500 Internal Server Eroor:服务端发生不可预估的错误
- 501 Server Unavailable:服务器不能处理当前客户端的请求,一段时间后可能恢复正常
30.分别从前端、后端、数据库阐述web项目的性能优化
前端:
- 尽量减少HTTP请求
- 将HTML和CSS放在页面上部,JS放在页面下部;因为JS加载比HTML和CSS慢,所以要优先加载HTML和CSS,以防页面加载不全,性能差,影响用户体验
后端:
- 缓存存储读写次数高,变化少的数据;应用程序读取数据时,先从缓存中读取,如果数据不存在或已失效,再读取磁盘数据库,并将数据再放入缓存
- 如果程序有耗时操作,采用异步
- 代码方面,尽量减少循环和if条件判断,有多个if else时,优先判断最有可能发生的情况
- 将动态页面换成html,用nginx访问
数据库:
- 选用合适的数据库,如果有条件可以将数据存放在redis,读写速度快
- 添加外键,索引等
31.使用pop和del删除字典中的”name”字段,dic={“name”:”zs”,”age”:18}
dic={'name':'zs','age':18}
#del dic['name]
dic.pop('name)
print(dic)
32.列出常见MYSQL数据存储引擎
- cookie存在客户端(浏览器),session运行在服务器端
- cookie的安全性比session差
- session的运行依赖sessionID,sessionID存放在cookie中,如果浏览器禁用了cookie,session就会失效;存储session时,session的键是cookie中的sessionID,值为开发人员添加的键值对,进行了base64编码,失效时间由开发人员设置
35.简述多线程、多进程
进程:
- 操作系统进行资源分配和调度的基本单位,多个进程之前相互独立
- 稳定性好,如果一个进程崩溃,不影响其他进程,但是进程消耗资源大,开启的进程数量有限制
线程:
- CPU进行资源分配和调度的基本单位,线程是进程的一部分,是比进程更小的能独立运行的基本单位,一个进程下的多个线程可以共享该进程下的所有资源
- 如果IO密集型,可以用多线程,缺点是如果一个线程崩溃,都会造成一个进程的崩溃
应用:
IO密集型用多线程,CPU密集型用多进程
36.简述any()和all()方法
any:迭代器中只要有一个元素为真(不为空或0),则返回True;否则,返回False,空迭代器也返回False
all:迭代器中全部元素为真(不为空或0),返回True;否则,返回False,空迭代器也返回True
37.IOError、AttributeError、ImportError、IndentationError、IndexError、KeyError、SyntaxError、NameError分别代表什么异常
- 复制不可变数据类型(元组、字符串、数值等):浅拷贝和深拷贝都和赋值 “=” 一样,id地址和值都跟被复制对象相同
- 复制可变数据类型(列表、字典):
- 浅拷贝分为两种情况:
- 拷贝对象无复杂子元素:修改拷贝对象元素,或者修改浅拷贝后的对象的元素,都对对方没有影响,但id地址不同
- 拷贝对象有复杂子元素:修改拷贝对象子元素,或者修改浅拷贝后的对象的子元素,都会影响对方的值,id地址不同
- 深拷贝是完全独立的复制,无论有无复杂子元素,修改拷贝对象元素或者修改深拷贝后对象的元素,都对对方没有影响,id地址不同
39.列出几种魔法方法并简要介绍用途
- __init__:构造函数,实例化对象时自动调用
- __new__:创建对象执行的方法,单例模式使用
- __str__:当对象被当做字符串使用时调用,最常用的就是print输出对象,会输入__str__方法中return的内容
- __del__:删除对象时执行的方法(删除对象:如果对象赋值了多个变量,删除其中一个变量对对象的引用并不会直接删除对象,必须删除所有对对象引用的变量才会删除对象)
40.请将[i for i in range(3)]改成生成器
将中括号换成小括号即可
# 列表生成式、推导式 a = [i for i in range(3)] print(type(a)) print(a) print("========================================") # 列表生成器 b = (i for i in range(3)) print(type(b)) print(b) for i in b: print(i)
41.使用lambda函数对list排序foo = [-5,8,0,4,9,-4,-20,-2,8,2,-4],输出结果为[0,2,4,8,8,9,-2,-4,-4,-5,-20],正数从小到大,负数从大到小
foo = [-5,8,0,4,9,-4,-20,-2,8,2,-4] # 方法一:列表拼接 print(sorted(filter(lambda num:num>=0,foo))+sorted(filter(lambda num:num<0,foo),reverse=True)) # 方法二: # 这里注意[::-1]和[:-1]的区别: # [::-1]表示可迭代对象元素的反转,比如a='python',a[::-1]输出'nohtyp',a[::-2]输出'nhy' # [:-1]表示可迭代对象最后一位之前的所有元素,比如b='java',b[:-1]输出'jav' print(sorted(filter(lambda num:num>=0,foo))+sorted(filter(lambda num:num<0,foo))[::-1]) # 方法三:根据sorted()方法的key参数,如果元素小于0则取绝对值排序 print(sorted(foo,key=lambda num:(num<0,abs(num))))
42.列表嵌套字典的排序,分别根据年龄和姓名排序
l = [{'name':'张三','age':18},{'name':'李四','age':20},{'name':'王五','age':6}]
print(sorted(l,key=lambda k:k['name'],reverse=True))
43.列表嵌套元组,分别按字母和数字排序
l = [('a',4),('b',3),('c',1)] print(sorted(l,key=lambda x:x[1]))
44.列表嵌套列表排序,年龄数字相同怎么办?
l = [['d',39],['aaa',66],['bbb',39],['ccc',18]] # 添加参数,如果第一个参数的值相同,则按照第二个参数的值进行排序 print(sorted(l,key=lambda num:(num[1],num[0])))
45.根据键对字典排序
d = {'name':'wawaw','sex':'1','age':18}
# d.items()生成的是一个列表嵌套的元组,每个元组里包含键值对
s = sorted(d.items(),key=lambda x:x[0])
# 字典生成式
print({i[0]:i[1] for i in s})
46.举例说明SQL注入和解决办法
当以字符串格式化书写方式的时候,如果用户输入的有“;SQL语句”,后面的SQL语句会执行,比如下面例子中的demo数据库会被删除
input_name = 'master' sql = 'select * from t_user where name="%s"' % input_name print("这是正常的SQL语句"+sql) input_name = 'master;drop database demo' sql = 'select * from t_user where name="%s"' % input_name print("这是SQL注入的语句"+sql)
解决方式:通过传参数方式解决SQL注入
input_name = 'master' # 通过参数化的方式解决 params = [input_name] # cs1为数据库的游标,execute()方法提供参数化的方式解决SQL注入 sql = cs1.execute('select * from t_user where name="%s"',params)
47.s=”info:xiaoZhang 33 shandong”,用正则切分字符串输出[‘info’, ‘xiaoZhang’, ‘33’, ‘shandong’]
import re s = " info:xiaoZhang 33 shandong" # |表示或 print(re.split(':| ',s))
48.正则匹配以163.com结尾的邮箱
import re email = ['a123@qq.com.qq','ab12@163.com','ddd@gmail.com','333@163.com.com'] for i in email: r = re.match('^[\w]+@163.com$',i) if r: print(r.group())
49.递归求和
# 求1-100的和 # 方法一:归并、缩减reduce(需要导入functools的reduce包) from functools import reduce l = [i for i in range(101)] print(reduce(lambda x,y:x+y,l)) # 方法二:递归调用 def get_sum(num,max_num): if max_num >= num: res = max_num + get_sum(num,max_num-1) else: res=0 return res print(get_sum(1,100))
50.python字典和json字符串相互转化方法
import json d = {'a':1,'b':2,'c':3} # 字典转json j = json.dumps(d) # json转字典 j1 = json.loads(j) print(j) print(type(j)) print(j1) print(type(j1))
51.MyISAM 与 InnoDB 区别
- InnoDB支持事务,MyISAM不支持。事务是一种高级的处理方式,例如增删改时如果出了问题可以回滚还原,而MyISAM就不可以
- InnoDB支持外键,MyISAM不支持
- InnoDB删除表是一行行的删除,效率慢;MyISAM则是重建表
- InnoDB适合频繁修改和安全性较高的应用;MyISAM适合以查询和插入为主的应用
- InnoDB需要为有自增字段创建一个独立的索引;MyISAM可以和其他字段一起建立联合索引
52.用两种方法去空格
s = ' A B C ' # 方法一 s1 = s.split() print(''.join(s1)) # 方法二 s2 = s.replace(' ','') print(s2)
53.正则匹配不是以4和7结尾的手机号
import re mobile = ['13456774019','15988139634','13144447777'] for m in mobile: # 中括号内^表示非,除了的意思 get_mobile = re.findall('[^47]$',m) if get_mobile: print(m) else: print('%s不符合要求' %m)
54.简述python引用计数机制
python垃圾回收是以引用计数为主,标记-清除和分代清除为辅;标记-清除和分代回收主要是为了处理循环引用的难题。
引用计数算法:
当有一个变量保存了对象的引用时,此对象的引用计数就会+1,当使用del删除变量指向的对象时,如果对象的引用计数不为1,比如3,那么引用计数就会-1,变为2;再次del变量指向的对象时,
变为1,如果再调用一次del,才会真正删除对象。
55.列举3条以上PEP8编码规范
- 顶级定义之间空两行,比如函数或者类定义
- 方法定义、类定义与第一个方法之间应该空一行
- 三引号进行注释
- 使用Pycharm或者Eclipse一般使用4个空格来缩进代码
56.正则匹配中文
import re title = 'hello,你好,world,世界!' result = re.findall('[\u4e00-\u9fa5]+',title) print(result)
57.简述乐观锁和悲观锁
悲观锁:每次去拿数据的时候都会认为别人会修改,所以每次拿数据的时候都会上锁,这样别人想拿这个数据就会block直到它拿到锁。传统的关系型数据库很多就用到了这种锁机制,
比如,行锁,表锁,读锁,写锁等,都是操作之前先上一把锁。
乐观锁:每次去拿数据的时候都不认为别人会修改,所以每次拿数据都不会上锁,但是在更新的时候会判断一下在此期间有没有人更新数据,可以使用版本号等机制,乐观锁适用于多
读的应用类型,这样可以提高吞吐量。
58.正则表达式匹配出<html><h1>www.baidu.com</h1></html>
import re url = ['<html><h1>www.baidu.com</h1></html>','<html><h1>www.baidu.com</h2></html>'] for i in url: # \1\2表示反向引用,即\1匹配第一个()内匹配到的内容,\2匹配第二个()内匹配到的内容,以此类推 get_str = re.findall(r'<(\w+)><(\w+)>.+?</\2></\1>',i) if get_str: print(i) else: print('%s不符合要求'%i)
59.python传参数是传值还是传址?
python参数都是引用传递,对于不可变数据类型(字符、数值、元组),因为变量不能修改,所以函数体内的运算不会影响到变量自身;对于可变数据类型(列表、字典),函数体运算会更改传入的参数变量
60.求两个列表的交集、差集、并集
l1 = [2,4,6,6,8,9,3] l2= [4,2,2,6,8,8] # 交集 print(list(set(l1).intersection(set(l2)))) # 并集 print(list(set(l1).union(set(l2)))) # 差集 print(list(set(l1).difference(set(l2))))
61.生成0-100之间的随机数
import random # 随机整数 print(random.randrange(0,101,1)) # 随机整数 print(random.randint(0,101)) # 随机整数 print(random.choice(range(101))) # 随机小数,random.random()生成0-1之间的随机小数,所以乘以100 print(100*random.random())
62.lambda匿名函数的好处
精简代码,省去了定义函数
l = ['abc','cba','aaa','ccc'] # map省去了for循环 print(list(map(lambda i:'我饿了' if i == 'abc' else i,l)))
63.常见的网络传输协议
HTTP、FTP、TCP、UDP、SMTP等等
64.单引号、双引号、三引号的用法
单引号和双引号没什么区别,表示字符串的时候单引号里面可以嵌套双引号,反之亦然;如果单引号(或者双引号)里面再使用单引号(或双引号),则需要转义,即在每个嵌套引号前面加反斜杠(\)
三引号可以书写多行,通常用于大篇幅的字符串
65.HTTP请求中GET和POST的区别
- GET请求通过url直接访问,所以数据信息在url中可以直接看到,比如浏览器访问;而POST请求是放在请求头中的,无法直接看到
- 少量数据使用GET,大量数据使用POST
- GET请求的安全性比较低,比如密码是不能直接暴露在url中的,就不能使用GET请求,而POST请求是放在请求头中的,可以使用;一般涉及到登录密码操作的,最好使用HTTPS,安全性更好
66.读取Excel
# 导入应用数据分析库 import pandas file = pandas.read_excel('123.xls') print(file)
67.简述解释型和编译型编程语言
解释型语言编写的程序不需要编译,在执行的时候专门有一个解释器,把解释型语言编译成机器语言,每个语句都是在执行的时候才编译,每执行一次编译一次,效率比较低
编译型语言在执行程序之前,会有一个专门的编译过程,编译系统把源高级程序编译成机器语言文件,只需翻译一次,执行时不需要编译,所以编译型语言效率比较高
(简单来说就是:解释型语言在执行时编译,每次执行都要编译;编译型语言执行前编译,只需编译一次)
68.Python 的解释器种类以及相关特点
- CPython
- 使用C语言开发,使用最广的解释器
- IPython
- 基于Cpython之上的一个交互式计时器,交互方式增强,功能和CPython一样
- PyPy
- 采用JIT技术,对python代码进行动态编译,提高执行效率
- JPython
- 运行在Java上的解释器,把Python代码编译为Java字节码
- IronPython
- 运行在微软.NET上的解释器,把Python代码编译为.NET字节码
69.字符串反转的几种方法
s = 'ilovechina' # plan1 切片 print(s[::-1]) # plan2 转换成列表反转 l = list(s) l.reverse() print(''.join(l)) # plan3 l2 = list(s) result = '' for _ in range(len(l2)): p = l2.pop() result += p print(result)
70.字符串编码转换
s = 'S' # 编码成二进制数据 bm = s.encode('gbk') print(bm) # 解码成二进制数据 jm = bm.decode('utf-8') print(jm)
71.打乱列表元素
import random l1 = [1,2,3,4,5,6] random.shuffle(l1) print(l1)
73.交换字典键值
# 方法一 字典生成式 d = {'name':'xiaoguo','age':18} d_new_1 = {v:k for k,v in d.items()} print(d_new) # 方法二 d_new_2 = dict() for k,v in d.items(): d_new_2[v] = k print(d_new_2) # 方法三 z = zip(d.values(),d.keys()) d_new_3 = dict(z) print(d_new_3)
74.我们知道对于列表可以使用切片操作进行部分元素的选择,那么如何对生成器类型的对象实现相同的功能呢?
# 导入itertools模块下的islice from itertools import islice # 斐波那切数列(前面两个数之和生成第三个数) def test(num): a,b = 0,1 while num > 0: a,b = b,a+b num -= 1 yield a for i in test(10): print(i) print('========================================================') # 普通生成器列表 for x in islice((i for i in range(10)),4,10): print(x)
75,生成器实现斐波那切数列
def test(num): a,b = 0,1 while num > 0: a,b = b,a+b num -= 1 yield a for i in test(10): print(i)
76.a="hello"和 b="你好"编码成 bytes 类型
a = 'hello' b = '你好' # 方法一 # 编码 en_1 = a.encode(encoding='utf-8') en_2 = b.encode(encoding='utf-8') print(en_1,en_2) # 解码 print(en_1.decode(),en_2.decode()) # 方法二 print(bytes(a,encoding='utf-8')) print(bytes(b,encoding='utf-8'))
77.在读文件操作的时候会使用 read、readline 或者 readlines,简述它们各自的作用
- read([size]):读取size个字节,没有参数表示读取到文件结束,输出的是字符串对象
- readline:读取一行内容,占用内存小,适合读取大文件,输出的是字符串对象
- readlines:读取文件所有行,保存在一个列表中,每行内容作为一个元素,读取大文件会比较占内存
78.json 序列化时,可以处理的数据类型有哪些?如何定制支持 datetime 类型?
可处理的数据类型:字符串(str)、整型(int)、布尔(bool)、列表(list)、元组(tuple)、字典(dict)、null
''' 序列化时间类型的数据 ''' import json from json import JSONEncoder from datetime import datetime class FormatTime(JSONEncoder): def default(self,obj): # 判断obj是否是datetime类型 # 如果是则格式化时间 if isinstance(obj,datetime): return obj.strftime('%Y-%m-%d %H:%M:%S') else: # 否则是其他类型的数据,调用父类默认的default方法 return JSONEncoder.default(obj) d = {'name':'gy','time':datetime.now()} get_time = json.dumps(d,cls=FormatTime) print(get_time)
79.json 序列化时,默认遇到中文会转换成 unicode,如果想要保留中文怎么办?
import json d = {'words':'你好'} print(json.dumps(d,ensure_ascii=False))
80.如果当前的日期为 20190530,要求写一个函数输出 N 天后的日期,(比如 N 为 2,则输出 20190601)
from datetime import timedelta from datetime import datetime def get_date(date_time,n): # 将字符串对象转换为时间格式对象,注意传入的字符串格式必须要个格式化的格式一样 format_time = datetime.strptime(date_time,'%Y%m%d') if isinstance(n,int): # datetime.timedelta:时间间隔,参数可以是days、hours等,以此类推 new_date = (format_time + timedelta(days=n)).strftime('%Y%m%d') return new_date else: return('输入的条件不符合要求') print(get_date('20161120',10))
81..写一个函数,接收整数参数 n,返回一个函数,函数的功能是把函数的参数和 n 相乘并把结果返回
# 闭包结构的调用 def a(m): def b(n): return m*n return b a1 = a(3) a1(2)
82.请写一段 Python连接 Mongo 数据库,然后的查询代码
import pymongo db_config = { "type":"mongo", "host":"主机地址", "post":"端口号", "username":"用户名", 'passwd':"密码", "db_name":"数据库名称" } class Mongo(): def __init__(self): self.db = db_config["db_name"] self.uname = db_config["username"] self.passwd = db_config["passwd"] # 字符串前面加f 表示支持字符串中大括号内的表达式 self.client = pymongo.MongoClient(f'mongodb://{db_config["host"]}:db_config["port"]') if self.uname and self.passwd: self.db1 = self.client[self.db].authenticate(self.uname,self.passwd) self.db1 = self.client[self.db] def get_data(self): # 查询状态为0的数据 data = self.db1.find({"status":"0"}) gen = (i for i in self.data) return gen m = Mongo() print(m.get_date())
83.Redis数据类型
redis指出五种数据类型:string(字符串)、hash(哈希)、list(列表)、set(集合)、zset(有序集合)
84.请写一段 Python连接 Redis 数据库的代码
import redis # redis默认端口号是6379 conn = redis.Redis(host="localhost",port="6379",decode_response="True") # 将键值存入redis缓存 conn.set('name','zhangsan') print(conn['name']) # 取出键对应的值 print(conn.get('name'))
85.请写一段 Python 连接 MySQL 数据库的代码
import pymsql try: conn = pymsql.connect(host="localhost",db='test',user="ceshi",pwsswd="ceshi",port='3306',charset='utf-8') # 建立游标 cur = conn.cursor() cur.excute("select * from t_user") # 获取所有数据,同时还有fetchone(只获取一条)、fetchmany(num)(获取num条数据) # 获取数据返回多个元组 data = cur.fetchall() for d in data: print('姓名:'+d[0],'年龄:'+str(d[1]),'联系方式:'+d[2]) # 关闭游标 cur.close() # 释放数据库连接 conn.close() except: print("连接数据库失败")
86.数据库三范式
- 第一范式(确保每列的原子性)
第一范式是最基本的范式,如果数据库表中所有字段都是不可分解的原子值,则表明该数据库满足了第一范式
- 第二范式(每列都和主键相关)
第二范式是在第一范式的基础上更进一步,确保数据表的每列都和主键相关,而不是只和主键部分相关(主要是针对联合主键);也就是说,一个数据库表中只能保存一种数据,而不是把多种数据都保
保存在一张表中
- 第三范式(每列都和主键直接相关)
第三范式是数据库表每列都和主键直接相关,而不是间接相关
87.Python 使用 Mongo 数据库创建索引
import pymongo url = 'mongodb://localhost:27017' db_name = 'test' mc = pymongo.MongoClient(url) mydb = mc[db_name] # 连接collection col = mydb['temp001'] ''' 创建mongo索引 如果不指定名称,系统会使用默认名称'index' 创建唯一索引需要加上unique=True 1:表示正序排序;-1:表示倒叙排序 ''' col.create_index('{"name":1,"mobile":1}',name='test_index')
88.函数装饰器有什么用,举例说明
作用:在其他函数不做任何代码变动的情况下增加额外的功能
from datetime import datetime def get_time(func): def wrapper(*args,**kwargs): now_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S') print('现在的时间是:'+ now_time) return func(*args,**kwargs) return wrapper @get_time def printHello(): print('hello world!') printHello()
89.__call__函数的用法
''' __doc__: 类(实例).__doc__ 类或实例的描述信息(注释) __module__: 类(实例).__module__ 表示对象属于哪个模块 __dict__:查看类或实例的所有属性,结果为字典 __class__: 表示当前操作对象的类 __str__:打印对象时显示 __call__:类(实例)加小括号调用时执行 ''' # __call__使用案例 class Reader(): def __init__(self,name,nationality): self.name = name self.nationality = nationality # 可以传参数 def __call__(self,age): print('Reader: %s Nationality: %s Age: %s' % (self.name, self.nationality,age)) r = Reader('张三','ada') r(20)
90.如何判断一个对象时函数还是方法
# 使用instance from types import MethodType,FunctionType def func1(): pass class TestClass(): def func2(self): pass tc = TestClass() print(isinstance(func1,FunctionType)) print(isinstance(tc.func2,MethodType))
91.@classmethod 和@staticmethod 用法和区别
''' 普通方法: 第一个参数self,表示对象本身,只能访问实例属性,不能访问类属性,实例名.方法名()调用 类方法:第一个参数cls,表示类本身,只能访问类属性,不能访问实例属性,不需要实例化,直接【类名.方法名()】,可通过类对象或类对象实例访问 静态方法:名义上归类管,实际上与类无关,只是类下面一个单独的函数,可以不传参数,不需要实例化,直接【类名.方法名()】,可通过类对象或类对象实例访问 ''' class Fruits(): p_name = 'xiaoguo' def __init__(self,f_name): self.f_name = f_name # 普通方法 def say(self): print('我是普通方法,至少要有一个参数self') print("我爱吃%s" % self.f_name) # 类方法 @classmethod def make(cls,f_name): print('我是类方法,至少要有一个参数cls') print('%s爱吃%s' %(cls.p_name,f_name)) # 静态方法 @staticmethod def other(f_name): print('我是静态方法,可以不传参数!') print('我也爱吃%s' %f_name) f1 = Fruits('芒果') f1.say() print("="*30) f2 = Fruits('苹果') f2.make('香蕉') Fruits.make('香蕉') print("="*30) f3 = Fruits('车厘子') f3.other('车厘子') Fruits.other('车厘子')
92.Python 中的接口如何实现
什么是接口:接口只是定义了一些方法,而没有去实现,多用于程序设计时,只是设计需要哪些功能,并没有实现这些功能;这些功能需要由另一个类继承后,实现部分或全部功能。
''' 接口的实现 ''' # 方法一:通过抽象类和抽象方法,抽象类只能被继承,不能被实例化 # 抽象类可以有普通函数和抽象函数,抽象函数需要被标识,且子类必须全部重写,否则会报错;普通函数在子类中也可以正常调用 from abc import ABCMeta,abstractmethod class Interface(metaclass=ABCMeta): @abstractmethod def a(self): pass @abstractmethod def b(self): pass def c(self): pass class Temp(Interface): def __init__(self): print('我是实现类') def a(self): print('我是方法A') def b(self): pass t = Temp() t.a() t.b() # c()是抽象类中的普通方法,子类也可以正常使用 t.c() # 方法二:普通类(推荐) class MyClass(): def func_a(self): pass def func_b(self): pass class Test(MyClass): def func_a(self): print("我是a") def func_b(self): print("我是b") tt = Test() tt.func_a() tt.func_b()
93.Python 中的反射了解么
- 定义:通过字符串映射object对象的方法或属性
- 方法
- hasattr():判断对象中是否有属性或者方法,返回布尔值
- setattr:给对象添加属性或方法
- getattr: 获取对象中变量或方法的内存地址
- delattr:删除对象中的属性,注意!不能删除对象方法!
class Test(): def __init__(self,name): self.name = name def say(self): print("%s say:Heelo World" %self.name) t = Test('zhangsan') t.say() # hasattr:判断对象中是否有属性或者方法,返回布尔值 print(hasattr(t,"name")) # True print(hasattr(t,"say")) # True print(hasattr(t,'abc')) # False # getattr: 获取对象中变量或方法的内存地址 n = getattr(t,'name') print(n) # 打印zhangsan f = getattr(t,'say') f() abc = getattr(t,'abc','not found') # 第三个参数表示没有找到指定的属性或方法时,显示的内容 print(abc) # setattr:给对象添加属性或方法 def abc(self): print("我是abc") setattr(t,'abc',abc) # 添加方法 t.abc(t) # 额外添加的方法要手动传入对象 setattr(t,'age',18) # 添加属性 print(t.age) # delattr:删除对象中的属性,注意!不能删除对象方法! delattr(t,"age") print(t.age) # 会报没有该属性的错
94.metaclass 作用以及应用场景
- 作用:metaclass用来创建类的,好比类是用来创建对象的;如果说类是对象的模板,那么metaclass就是类的模板
- 应用场景:暂时没能理解
95.什么是猴子补丁
''' 定义:在运行时动态修改模块、类或函数,通常是添加功能或者修正缺陷 ''' class A(): def func(self): print('hello') def monkey(self): print("hello world") A.func = monkey a = A() a.func()
96.在 Python 中是如何管理内存的
- 对象的引用计数机制(使用引用计数来追踪内存中对象,python内部记录了对象有多少个引用,即引用计数)
- 增加引用计数的情况
- 创建对象:x=123
- 赋值:y = x
- 对象被作为参数传递给函数(新的本地引用):func(x)
- 对象成为容器对象的一个元素:myList = [x,123,'abc']
- 减少引用计数的情况
- 对象的别名被赋值给了其他对象:x = 456
- 显示的销毁对象:del x
- 离开本地作用域:比如func()函数结束时,里面的局部变量(全局变量不会)
- 窗口对象中移除对象:myList.remove(x)
- 窗口对象被销毁:del myList
- 增加引用计数的情况
- 垃圾回收机制
- 当引用计数变为0时,说明没用任何引用指向该对象,此时该对象就会被当做垃圾回收
- 垃圾回收主要是以引用计数为主,标记清除和分代清除为辅的机制,分代清除和标记清除是为了处理循环引用的问题
- 内存池机制
- python对内存提供了垃圾收集机制,但是它将不用的内存放到了内存池,而不是返回给操作系统
- python中分为大内存和小内存(256k为界限)
- 大内存使用malloc分配内存空间
- 小内存使用内存池分配空间
- python针对内存管理机制有两套实现:小于256K的内存(即小内存),使用pymalloc在内存池中申请空间;大于256K的内存(即大内存)直接执行系统的malloc行为申请内存空间
97.当退出 Python 时是否释放所有内存分配
不会。循环引用其他对象或者引用自全局命名空间对象的模块,在python退出时并非全部释放;另外,也不会释放C库保留的内存部分
98.python中的作用域
作用域就是一个 Python 程序可以直接访问命名空间的正文区域
python作用域遵循LEGB原则,优先级L>E>G>B,即:
L(Local):局部作用域
E(Enclosing):闭包函数外的函数中
G(Global):全局作用域
B(Built-in):内建作用域
# dir为Python内建函数 dir = 1 # Global def test(): dir = 2 # Enclosing def wrapper(): dir = 3 #Local return dir return wrapper print(test()()) # 输出3
作用域和命名空间的关系:作用域一定是命名空间,但命名空间不一定是作用域。
99..三元运算写法和应用场景
写法:result = 返回结果 if 条件 else 条件不满足返回结果
应用场景:简化if条件语句,比如lambda表达式
res = 'true' if 2 < 1 else 'false' print(res) # 打印'false' res1 = lambda x: x if x > 0 else '404' print(res1(-1)) # 打印404
100.了解enumerate么
系统的一个内置函数,为可迭代对象里每个元素配上一个索引,索引与每个元素组成tuple类型
l = ['姓名','班级','带班老师'] e = [i for i in enumerate(l)] print(e) # [(0, '姓名'), (1, '班级'), (2, '带班老师')]
101.pathlib 的用法举例
较常用的用法是导入pathlib模块中的Path类,可以读取或拼接路径并转换成当前系统下的分隔符,用来操作文件。
from pathlib import Path p = Path() # 输出'.' p1 = Path('a','b','c','d') # 输出 a\b\c\d print(p1.parts) # 返回路径中每一个部分 ('a', 'b', 'c', 'd') p2 = p1 / 'e' # 操作符'/' 拼接 print(p2) # 输出a\b\c\d\e data_folder = Path("source_data/text_files/") # 输出source_data\text_files file_to_open = data_folder / "raw_data.txt" with open(file_to_open,'rw+',encoding='utf-8') as f: print(f.read())
102.Python 中递归的最大次数,如何突破
最大递归深度为999
import sys sys.setrecursionlimit(10000) # 设置最大递归深度为10000
103.什么是面向对象的 mro
对于支持继承的编程语言来说,其方法(属性)可能定义在当前类,也可能定义在基类;所以在方法调用的时需要对当前类和基类进行搜索以确定方法所在的位置,这种搜索的顺序就是所谓的方法解析
顺序(即MRO)。对于只支持单继承的编程语言来说,MRO就比较简单,而对于Python这种支持多继承的语言来说,MRO就复杂的多。
104.isinstance作用以及应用场景
作用:判断一个对象是否是一个已知的类型
l = [1,2,3] print(isinstance(l,list)) # True
105.什么是断言以及应用场景
assert,是一种调试工具,用来测试某个断言条件;如果条件为真,则程序继续正常执行,如果条件为假,则会报AssertionError,并显示相关错误信息。
106.新式类和旧式类的区别
python有两种类:经典类和新式类;两者不同之处在新式类继承自object,python2.2引入了新式类,但在python3中,新式类是唯一支持的类。
107.dir()是干什么用的
dir():如果没带参数,将返回当前范围内的变量,方法和定义的类型列表;如果带参数,则返回参数的属性和方法,如果参数中包含__dir__()方法,则该方法将被执行,如果不包含,则最大限度的收集
参数的信息。
108.一个包里有三个模块,demo1.py, demo2.py, demo3.py,但使用 from tools import 导入模块时,如何保证只有 demo1、demo3 被导入了
可以在__init__.py文件中添加__all__=['demo1','demo3']
109.代码中经常遇到的*args, **kwargs 含义及用法
这两个是python中的可变参数,*args表示任意多个参数,是一个tuple;**kwargs表示关键字参数,是dict,如果同时使用*args和**kwargs时,*args必须在**kwargs前面。
110.Python 中会有函数或成员变量包含单下划线前缀和结尾,和双下划线前缀结尾,区别是什么
- 前置单下划线_var:命名约定,表示该名称仅内部使用
- 后置单下划线var_:命令约定,避免和python关键字发生冲突
- 前置双下划綫__var:在类环境使用时会触发名称改写,对python解释器有特殊含义
- 前后双下划綫__var__:表示python定义的特殊方法,在自定义属性时要避免使用这种命名方式
111.举例 sort 和 sorted 的区别
sort()是在原位重新排列一个列表,sorted()是重新生成一个列表
112.什么是负索引
负索引是从最右边开始检索数据,例如,-1是获取列表最后一个数据,-2是获取倒数第二个数,以此类推。
113.pprint模块是干什么的
print()和pprint()都是python的打印模块,功能基本一致,唯一的区别,pprint()是分行打印输出结果,方便查看,print()打印在一行;对于数据结构复杂,数据长度较长的数据,推荐使用pprint()
114.解释一下python中的赋值运算符
python中使用“=”给变量赋值,在算数运算中,为了简化代码编写,Python还提供了一系列赋值运算符,例如,b += a,等价于b = b + a
115.解释一下python中的逻辑运算符
and、or、not
116.讲讲python中的位运算符
该运算符按二进制位对值进行操作
- &:按位与运算符,参与运算的两个值,如果对应位都为1,则该位结果为1,例如(110011 & 001111),返回结果000011
- |:按位或运算符,参与运算的两个值,如果对应位有一个为1,则该位结果为1,上面例子返回111111
- ^:按位异或运算符,当参数运算的两个值对应位相异时,结果为1
- ~:按位取反运算符,对数据的每个二进制位取反,即把1变成0,0变成1
- <<:左位移,数据的每个二进制位向左移若干位,由<<右边的数字指定移动的位数,高位丢弃,低位补0;例如,001100 << 2,返回结果 110000
- >>:右位移,数据的每个二进制位向右移若干位,由>>右边的数字指定移动的位数,低位丢弃,高位补0;例如,001100 << 2,返回结果 000011
117.python中如何使用多进制数
- 二进制:由0或1数字组成,0b或0B开头表示二进制数,使用bin()函数将数字转换成二进制形式
- 八进制:由0-7数字组成,0o或0O开头表示八进制数,使用oct()函数将数字转换成八进制形式
- 十六进制:由数字0-15组成,0x或0X开头表示十六进制数,使用hex()函数将数字转换成十六进制形式
118.已知AList=[1,2,3],BSet={1,2,3} ,从AList和BSet中查找4,哪个最坏时间复杂度大?从AList和BSet中插入4,哪个最坏时间复杂度大?
最坏时间复杂度:算法完成工作,最多需要多少基本操作
常见的时间复杂度高低排序:O(1)<O(logn)<O(n)<O(nlogn)<O(n2)<O(n2logn)<O(n3)
查找:列表的最坏时间复杂度是O(n),集合是O(1),所以列表大
插入:两个都是O(1)
119.用 Python 实现一个二分查找的函数
二分查找:二分搜索是一种在有序数组中查找某一特定元素的搜索算法。搜索过程从数组的中间元素开始,如果中间元素正好是要查找的元素,则搜索过程结束;如果某一特定元素大于或者小于中间元
素,则在数组大于或小于中间元素的那一半中查找,而且跟开始一样从中间元素开始比较。如果在某一步骤数组为空,则代表找不到。这种搜索算法每一次比较都使搜索范围缩小一半。
优点:比较次数少,查找速度快,平均性能好
缺点:待查序列必须为有序,且不经常变动(插入和删除)
''' 二分查找(升序) arr:传入的序列 data:需要查找的数据 ''' def binarySearch(arr,data): len_arr = len(arr) mid = len_arr // 2 if len_arr >= 1: if arr[mid] > data: return binarySearch(arr[0:mid],data) elif arr[mid] < data: return binarySearch(arr[mid+1:],data) else: return True # 列表长度小于1,代表查询结束,没有找到需要的数据 else: return False l = [1,2,3,4,5] print(binarySearch(l,2)) # True print(binarySearch(l,9)) # False
120.找出列表中的重复数字
l = [2,1,3,3,6,2,4,7,2] # 方法一 from collections import Counter print(Counter(l)) # 打印Counter({2: 3, 3: 2, 1: 1, 6: 1, 4: 1, 7: 1}) # 方法二 d = {} for i in l: d[i] = l.count(i) print(d) # 方法三 s = set(l) for i in s: print('%s有%s个' %(i,l.count(i)))
121.写一个冒泡排序、快速排序、拓扑排序
- 冒泡排序
- 原理:一组无序的列表,遍历整个列表,依次比较相邻的两个数,如果后面的数比前面的小,则两数交换位置,第一轮排序过后最大的数字就被排到了末尾,下一轮只用遍历到倒数第二个数即可,以此类推,直到整个列表变为有序(升序)列表即可退出循环。
- 快速排序
- 原理:在一组序列中选取一个基准值(可以随便选,最好选取中间位置的值),循环查找出比基准值小的放在左边,比基准值大的放在右边,然后在左右两边递归重复上面的步骤,知道列表的长度为1或0表示排序结束,返回列表。
- 缺点:
- 基准值的选取对排序的性能有决定性的影响
- 不稳定,比如基准值前后都有和基准值相同的数据,那么相同值就会被放在一边,打乱了之前的相对顺序
- 对于小规模的数据集性能不是很好
l = [33,218,7,96,45,11,4,99,63,28] # 冒泡排序 def bubbling_sort(l): len_l = len(l) # 外层循环控制循环的次数 for i in range(len_l - 1): # 内层循环控制遍历到哪一位 for j in range(len_l - 1 - i): # 如果后一位比前一位小,则交换位置 if l[j+1] < l[j]: l[j],l[j+1] = l[j+1],l[j] return l # 快速排序 def fast_sort(l): # 如果列表的长度等于0或1表示排序结束,返回列表 if len(l) < 2: return l else: # 选取基准值 mid = l[len(l) // 2] # 先把基准值从列表中排序,不然会有重复 l.remove(mid) left = [i for i in l if i < mid] right = [j for j in l if j > mid] return fast_sort(left) + [mid] + fast_sort(right) print('冒泡排序的结果:%s' %bubbling_sort(l)) print('快速排序的结果:%s' %fast_sort(l))
122.python 实现一个二进制计算
a = '10010' # 18 b = '11000' # 24 a,b = int(a,2),int(b,2) # int()第二个参数表示传入的字符串进制数,默认十进制,返回结果十进制 print(bin(a+b)[2:]) # bin(a+b) 返回0b101010,所以要从第三位开始取
123.有一组“+”和“-”符号,要求将“+”排到左边,“-”排到右边,写出具体的实现方法
def symbol_sort(symbols): l1 = list() l2 = list() for s in symbols: if s == '+': l1.append(s) else: l2.append(s) l1.extend(l2) return l1 sym = '+--+++-' print(symbol_sort(sym))
124.单链表反转
反转链表的图例,便于理解:https://blog.csdn.net/songyunli1111/article/details/79416684
class ListNone(): def __init__(self,val): self.val = val self.next = None ''' 主要逻辑步骤: 1.先把第一个节点后的数据保存赋值给一个变量 2.把第一个变量的next赋值为空,即从链表分裂掉第一个节点 3.把声明的指针指向分裂出来的节点 4.最后把链表的头部指针赋值给下一个节点,即指针往后移动一位 如此循环...... ''' def reverseList(head): last = None while head: tmp = head.next head.next = last last = head head = tmp return last l = ListNone(1) l.next = ListNone(3) l.next.next = ListNone(9) l1 = reverseList(l) print(l1.val,l1.next.val,l1.next.next.val) # 打印 9 3 1
125.交叉链表的交叉点(若两个链表有一个公共结点,则该公共节点之后的所有结点都是重合的,及它们的最后一个结点必然重合)
''' 交叉链表逻辑: 若两个链表有一个公共结点,则该公共节点之后的所有结点都是重合的,及它们的最后一个结点必然重合(结构是Y字形) 利用这个特性,可以先获取两个列表的长度,然后长的列表比短的链表先走(len(长)-len(短))个长度,然后两个链表的指针同时往后走 直到找到公共节点,没有找到相交节点返回None ''' class ListNode(): def __init__(self,val,next=None): self.val = val self.next = next # 获取链表长度 def get_length(head): length = 0 while head: length += 1 head = head.next return length # 获取公共节点 def get_cross_node(list_a,list_b): len_a = get_length(list_a) len_b = get_length(list_b) # 给两个链表各声明一个指针 cur_a = list_a cur_b = list_b if len_a > len_b: for i in range(len_a - len_b): cur_a = list_a.next else: for i in range(len_b - len_a): cur_b = list_b.next flag = False while cur_a and cur_b: if cur_a.val == cur_b.val: print(cur_a.val) flag = True break else: cur_a,cur_b = cur_a.next,cur_b.next if not flag: print("链表没有交叉节点") list_a = ListNode('a1',ListNode('a2',ListNode('c1',ListNode('c2',ListNode('c3'))))) list_b = ListNode('b1',ListNode('b2',ListNode('b3',ListNode('c1',ListNode('c2',ListNode('c3')))))) get_cross_node(list_a,list_b)
126.用队列实现栈
''' 栈:先进后出 队列:先进先出 方法一: 逻辑: 进栈push:在一个空的队列中添加元素,然后把另一个非空队列中的元素弹出并追加到该队列后面,所以,总会有一个队列是空的,以此循环追加即可 删除栈顶元素pop: 进栈时已把元素重新排序,所以直接弹出栈顶元素即可 方法二: 使用列表的insert()方法,指定每次插入的元素都放在最前面 ''' # 方法一 class Stack1(): def __init__(self): self.queue_a = [] self.queue_b = [] # 进栈 def push(self,n): if len(self.queue_a) == 0: self.queue_a.append(n) elif len(self.queue_b) == 0: self.queue_b.append(n) # 把队列b中的元素追加到a的后面 if len(self.queue_a) == 1 and len(self.queue_b) > 1: for i in range(len(self.queue_b)): self.queue_a.append(self.queue_b.pop(0)) elif len(self.queue_b) == 1 and len(self.queue_a) >= 1: for i in range(len(self.queue_a)): self.queue_b.append(self.queue_a.pop(0)) # 删除栈顶元素 def pop(self): if self.queue_a: return self.queue_a.pop(0) elif self.queue_b: return self.queue_b.pop(0) else: return None # 查询当前栈内元素 def get_stack(self): if self.queue_a: return self.queue_a elif self.queue_b: return self.queue_b else: return None # 查询栈顶元素: def peek(self): if self.queue_a: return self.queue_a[0] elif self.queue_b: return self.queue_b[0] else: return None # 查询当前栈长度 def len_stack(self): if self.queue_a: return len(self.queue_a) elif self.queue_b: return len(self.queue_b) else: return None # 方法二 class Stack2(): def __init__(self): self.list_a = list() def push(self,num): self.list_a.insert(0,num) def get_stack(self): return self.list_a s = Stack1() s2 = Stack2() for i in range(5): s.push(i) s2.push(i) print(s.get_stack()) # [4,3,2,1,0] print(s.pop()) print(s.get_stack()) print(s.pop()) print(s.get_stack()) print(s.pop()) print(s.get_stack()) print('*'*50) print(s2.get_stack()) # [4,3,2,1,0]
127.找出数据流的中位数
- 中位数:有序列表中间的数。如果列表元素个数是偶数,中位数是中间两个数的平均数;如果是奇数,则就是中间的数
def get_mid(l): l.sort() if len(l) == 0: return elif len(l) == 1: return l[0] else: if len(l) % 2 == 0: mid = (l[len(l) // 2] + l[len(l) // 2 - 1]) / 2 else: mid = l[len(l) // 2] return mid l = [2,15,7,3,100,10] print(get_mid(l)) # 8.5
128.二叉搜索树中第 K 小的元素
- 关于二叉树:https://www.jianshu.com/p/9503238394df
- 关于二叉搜索树:https://www.cnblogs.com/lliuye/p/9118591.html
- 算法:利用二叉搜索树特点(左树都比根节点小,右树都比根节点大),使用中序遍历后结果保存在列表中,其结果一定是有序排序,找出第K-1位元素即可
class Node(): def __init__(self,n): self.val = n self.left = None self.right = None ''' 数据域:节点中存储数据元素的部分 指针域:节点中存储数据元素之间的链接信息,即链接到下一节点地址的部分 内嵌函数: 内部函数需要递归调用并不能实现全部功能,外层函数完成剩余功能,也可以分开写 ''' def kthSmallest(root,k): # l = list() def search_min(root): if not root: return [] # 在内层函数申明列表是因为只需返回最后一次递归的结果,即[10, 12, 15, 16, 20, 21, 22, 23] # 若是申明在外部,则会把每一层递归的结果都追加的列表里 [10,10,12,10,10,12,15,10,10,12,15,16........] l = list() # 递归查询左树数据域 l.extend(search_min(root.left)) l.append(root.val) # 递归查询右树数据域 l.extend(search_min(root.right)) return l return search_min(root)[k-1] n1 = Node(20) n1.left = Node(15) n1.right = Node(22) n1.left.left = Node(12) n1.left.right = Node(16) n1.left.left.left = Node(10) n1.right.left = Node(21) n1.right.right = Node(23) kthSmallest(n1,6) # 21
129.TCP 和 UDP 的区别
- TCP协议在传送数据段的时候要给段标号;UDP不用
- TCP协议可靠;UDP不可靠
- TCP协议是面向连接;UDP采用无连接
- TCP协议负载较高,采用虚电路;UDP采用无连接
- TCP协议发送方要确认接收方是否收到数据段(三次握手协议)
- TCP协议采用窗口技术和流控制
130.简要介绍三次握手和四次挥手
- 三次握手
- 第一次握手
- Client端首先发送一个SYN包告诉Server端我的初始序列号是X,Client进入SYN-SENT(同步已发送)状态
- 第二次握手
- Server端收到SYN包后回复给Client端一个ACK确认包,告诉Client端我收到了,Server端进入SYN-RCVD(同步收到)状态
- Server端也需要告诉Client端一个初始序列号,于是,Server端也发送一个SYN包告诉Client端我的初始序列号是Y
- 第三次握手
- Client收到后,回复给Server端一个ACK确认包说我知道了,之后Client和Server进入ESTABLISHED(已建立连接)状态,完成三次挥手
- 第一次握手
- 四次挥手
- 第一次挥手
- Client端发送一个FIN包告诉Server端我的初始序列号是X,用来关闭Client到Server的数据传送,Client端进入FIN_WAIT_1(主动关闭,已经发送关闭请求,等待确认)状态
- 第二次挥手
- Server端收到FIN包后,发给Client端一个ACK确认包,Server端进入CLOSE_WAIT(被动关闭,收到对方关闭请求,已经确认)状态
- 第三次挥手
- Server端发送一个FIN包告诉Client端我的序列号是Y,用来关闭Server端到Client端的数据传送,同时发送给Client一个ACK确认包,Server端进入LAST_ACK(被动关闭,等待最后一个关闭确认,并等待所有分组死掉)状态
- 第四次挥手
- Client端收到FIN包后进入TIME_WAIT(完成双向关闭,并等待所有分组死掉)状态,接着发给给Server端一个ACK确认包,Server端进入CLOSED(关闭状态,没有连接活动或正在进行)状态,完成四次挥手
- 第一次挥手
扩展:三次握手改成两次握手可以吗?不可以,主要防止已经失效的请求连接突然又传送到了服务器,从而产生错误
132.TIME_WAIT状态的作用
主动关闭的那端最后经历的状态,一般为2MSL秒(1~4分钟)
- 保证当最后一个ACK包丢失后,能收到对端重新传的FIN包
- 保证ACK包消失,不会影响下一个连接
132.什么是粘包? socket 中造成粘包的原因是什么? 哪些情况会发生粘包现象?
定义:在接收数据时,一次性多接收了其他请求发送来的数据(即多包接收)。比如,对方第一次发送hello,第二次发送world,在接收时应该接收两次,一次是hello,一次是world,但事实上一次收到
helloworld,一次收到空,这种现象叫粘包。
原因:主要是因为接收方不知道消息之间的界限,不知道一次性提取多少字节的数据造成的
会发生的情况:
- 发送端需要等缓存区满才发送出去,造成粘包(发送数据时间间隔很短,数据很小,会合到一起,产生粘包)
- 接收方不及时接收缓存区的包,造成多个包接收(客户端发送了一段数据,服务端只接收了一小部分,服务端下次再收的时候还是从缓存区拿上次遗留的数据,产生粘包)
133.举例说明 concurrent.futures 中的线程池的用法
concurrent.futures模块的基础是Exectuor,Exectuor是一个抽象类,不能直接被调用,它的子类ThreadPoolExectuor和ProcessPoolExectuor,分别用来创建线程池和进程池,可以将相应的tasks放入相应
的线程池/进程池,不需要维护Queue来操心死锁的问题,线程池/进程池会自动调度
from concurrent.futures import ThreadPoolExecutor import time def ret_msg(msg): time.sleep(2) return msg pool = ThreadPoolExecutor(max_workers=2) # 最多加入两个task future1 = pool.submit(ret_msg,("hello")) # 加入task1 future2 = pool.submit(ret_msg,("world")) # 加入task2 print(future1.done()) # 查看task1任务是否完成 打印False time.sleep(3) print(future2.done()) # 打印True print(future1.result()) # 查看task1结果 print(future2.result())
134.说一说多线程,多进程和协程的区别
- 线程与进程比较
- 地址空间:线程是进程内的一个执行单元,进程内至少有一个线程,它们共享进程的地址空间,而进程有自己独立的地址空间
- 资源拥有:进程是资源分配和拥有的单位,同一个进程内的线程共享进程的资源
- 线程是处理器调度的基本单位,但进程不是
- 二者均可并发执行
- 每个独立的线程有一个程序运行的入口,顺序执行序列和程序的出口,但是线程不能独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制
- 线程与协程比较
- 一个线程可以多个协程,一个进程也可以单独拥有多个协程,这样python中则能使用多核CPU
- 线程进程都是同步机制,而协程是异步
- 协程能保留上一次调用时的状态,每次过程重入时,就相当于进入上一次调用的状态
135.进程之间如何通信
共享内存、信号量、互斥锁、消息队列等
136.IO 多路复用的作用
I/O多路复用就通过一种机制,可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作。但select,poll,epoll本质上都是同步I/O,因为他们都需要
在读写事件就绪后自己负责进行读写,也就是说这个读写过程是阻塞的,而异步I/O则无需自己负责进行读写,异步I/O的实现会负责把数据从内核拷贝到用户空间
137.select、poll、和epoll模型的区别
- select, poll是为了解決同时大量IO的情況(尤其网络服务器),但是随着连接数越多,性能越差
- epoll是select和poll的改进方案,在 linux 上可以取代 select 和 poll,可以处理大量连接的性能问题
138.什么是并发和并行
- 并行是指两个或者多个事件在同一时刻发生;而并发是指两个或多个事件在同一时间间隔发生
- 并行是在不同实体上的多个事件,并发是在同一实体上的多个事件
139.一个线程 1 让线程 2 去调用一个函数怎么实现
import threading def func1(t2): print("我是func1") t2.start() t2 = threading.Thread() t1 = threading.Thread(target=func1,args=(t2,)) t1.start()
140.解释什么是异步非阻塞
当一个异步过程调用发出后,调用者不会立刻得到结果。实际处理这个调用的部件是在调用发出后,通过状态、通知来通知调用者,或通过回调函数处理这个调用;非阻塞的意思是,不能立刻得到结果
之前,该函数不会阻塞当前线程,而会立刻返回
141.threading.local 的作用
Python提供了 threading.local 类,将这个类实例化得到一个全局对象,但是不同的线程使用这个对象存储的数据其它线程不可见(本质上就是不同的线程使用这个对象时为其创建一个独立的字典)
142.说说你知道的 git 命令
- git clone:从远端库克隆代码到本机
- git push:将本地仓库的代码推送到远端库
- git init:创建一个git仓库,创建之后会在当前目录生成一个.git文件
- git status:查看git库的状态,未提交的文件分为两种:add过已经在缓存区的,未add过的
- git add:把文件添加到缓存区
- git commit:提交缓存区的所有修改到仓库
- git rm:删除文件
143.git 如何查看某次提交修改的内容
知道commit id的情况下:
1. 获取commit id
git log
2. 查看commit内容
git show commit_id
查看最近n次提交的修改
git log -p -n
指定n为1则可以查看最近一次修改的内容
并行”指两个或两个以上事件或活动在同一时刻发生。在多道程序环境下,并行性使多个程序同一时刻可在不同CPU上同时执行

浙公网安备 33010602011771号