目录
15、python中的 __new__ 和 __init__ 的区别
25、列表[1,2,3,4,5],编辑一个函数输出[1,4,9,16,25],并取出大于10 的数,最终输出[16,25]
26、在一个坐标系上有两个矩阵,坐标分别是: 矩阵1:左上角(lx1, ly1),右下角(rx1, ry1) 矩阵2:左上角(lx2, ly2),右下角(rx2, ry2) 求两个矩阵重叠的面积。
1、TCP通信的三次握手
三次握手的目的是连接服务器的指定端口、建立TCP连接、同步双方的序列号和确认号、交换TCP窗口的大小信息,在socket编程中,客户端指定connect()时将触发三次握手。
- 第一次握手:建立连接时,客户端发送syn包到服务器,并进入SYN_SENT状态,等待服务器确认。(SYN:同步序列编号);
- 第二次握手:服务器收到客户端的syn包,必须向客户端发送确认信号(ack),并且向客户端发送syn包,此时服务器进入SYN_RECV状态;
- 第三次握手:客户端收到服务器的syn+ack,向服务器发送确认包ack,此包发送完毕,客户端和服务器进入ESTABLISHED状态(tcp连接成功),完成三次握手。
2、tornado框架如何处理http请求的
- 首先获取用户请求数据;
- 根据用户请求URL进行路由匹配,从而使得某个方法处理具体的请求;
- 将处理后的数据返回给客户端;
- 关闭客户端socket;
3、解释一下uWSGI
开头之前先讲解一下WSGI、uwsgi与uWSGI三者之间的区别:
- WSGI,是一种通信协议;
- uwsgi,也是一种通信协议;
- uWSGI,是实现了uwsgi和WSGI两种协议的Web服务器。
WSGI,全称Web Server Gateway Interface,是为python语言定义的web服务器和web应用程序或框架之间的一种简单而通用的接口。它是一个网关,作用是在协议之间进行转换。一边连接web服务器,另一边连接用户的应用。由于nginx具备优秀的静态内容处理能力,将动态内容转发给uWSGI服务器,这样可以达到很好的客户端相应。
uWSGI,是一个web服务器,它实现了WSGI,uwsgi,http等协议。Nginx中HttpUwsgiModule的作用是与uWSGI服务器进行交换。
uwsgi,是一个uWSGI服务器自有的协议,它用于定义传输信息的类型,每一个uwsgi packet前4byte为传输信息类型描述。
4、解释一下nginx
//nginx的源码目录与nginx的模块化以及功能的区分是紧密结合的,看一下nginx的目录结构:
5、re正则表达式
5.1 正则表达式中match和search的区别
相同点:都是在一个字符串中寻找pattern字符串,如果找到就返回Match对象,找不到返回None;
不同点:match方法是从头开始匹配,而search方法是在字符串的任一位置查找。
5.2 group 与groups的区别
group(N)可以返回第N组括号匹配的字符;
groups()返回所有括号匹配的字符,返回格式是一个元组;group() == (group(0),group(1).....)
5.3 符号 .* 与 .*? 的区别
.* 是贪婪匹配,一个符号匹配任意次;
.*? 是懒惰匹配,满足条件的符合只匹配一次;
6、django的并发和多线程
django框架本身只有一个线程在处理请求,任何一个请求阻塞,就会影响另一个请求的相应。python的多线程对于多核CPU利用率不高。django的并发通常配合nginx+uWSGI使用。
7、http与tcp的关系
TCP/IP协议是传输层协议,主要解决数据如何在网络中传输,而HTTP协议是应用层协议,主要解决如何包装数据的问题。
8、Linux日志文件中关键词查找命令
8.1 查看日志前n行
cat 日志文件名 | head -n 数量 ,如:
cat a.log | head -n 200 //查看前200行日志
8.2 查看日志后n行
cat 日志文件名 | tail -n 数量 ,如:
cat a.log | tail -n 200
8.3 关键字搜索命令(返回含有关键字的行)
cat 日志文件名 | grep "关键次"
cat a.log | grep "keyword" // 返回含有 keyword的所有行
或者 grep -i "关键字" 日志文件名:
grep -i "keyword" a.log
9、python中的装饰器
python装饰器就是用于扩展原来函数功能的一种函数,它的返回值也是一个函数(注意一个函数后面不加括号是对象,加上括号代表执行)。常用于插入日志、性能测试、事物处理、缓存、权限校验等场景。使用装饰器可以抽离出大量与函数功能无关的雷同代码并继续重用。
例如:给函数增加一个计时的功能
# 原来的函数
def func():
print('resource func')
给这个func()增加一个装饰器,用以统计函数的执行时间:
import time
def decorate(func):
def wrapper():
start_time=time.time()
func()
end_time=time().time()
msecs=end_time-start_time
print(msecs)
return wrapper
@decorate
def func():
print('resource func')
如果原函数有多个参数:
import time
def decorate(func):
def wrapper(*args,**kwrags):
start_time=time.time()
func(*args,**kwrags)
end_time=time().time()
msecs=end_time-start_time
print(msecs)
return wrapper
@decorate
def func(a,b):
print('resource func')
备注:多个装饰器执行的顺序是从最后一个装饰器开始,执行到第一个装饰器,再执行函数本身。
10、 git commit后,代码回滚操作
git reset --hard HEAD^ # 回退到上个版本
git reset --hard HEAD~3 # 回退到前3次提交之前
git reset --head commit_id # 回退或者前进到指定commit的SHA码
11、两个字典合并、两个列表合并
合并两个字典:
# 字典a,字典b
# 方法1:
new=dict(a,**b)
# 方法2:
new=dict(a.items()+b.items())
# 方法3:
new=a.update(b)
合并两个列表:
# 列表a ,列表b
# 方法1:
new=a+b
# 方法2:
new=a.extend(b)
# 方法3:
new=a.append(b)
# 方法4,使用切片:
a[0:0]=b 等于a.extend(b)
12、什么是lambda函数
lambda函数也叫匿名函数,该函数可以包含任意数量的参数,但是只能有一个执行操作的语句。
13、迭代器与生成器
迭代器:是可以遍历或迭代的对象;
生成器:返回可迭代项集的函数称为生成器;
14、如何解决循环导入的问题
1、直接导入模块名,通过模块调用其中的函数
2、使用延迟导入(lazy import),在需要用的函数内部导入,或者在底部导入
3、重新设计代码结构,将代码合并或者分离
15、python中的 __new__ 和 __init__ 的区别
- __new__ 是在实例被创建之前被调用的,它的任务是创建一个实现然后返回该实例对象,是个静态方法;
- __init__ 是当实例对象创建完成后被调用的,然后设置对象属性的一些初始值,通常在初始化一个类实例的时候,是一个实例方法;
- 也就是 __new__ 先被调用, __init__ 后被调用, __new__ 的返回值(实例)将传递给 __init__ 方法的第一个参数,然后 __init__ 给这个实例设置一些参数。
例如: __new__ 和 __init__ 的区别,说法正确的是:(ABCD)
A. __new__ 是一个静态方法,而 __init__ 是一个实例方法;
B. __new__ 方法会返回一个创建的实例,而__init__ 什么都不返回;
C. 只有在 __new__ 返回一个cls实例时,后面的 __init__ 才能被调用;
D. 当创建一个新实例时调用 __new__,初始化一个实例时用 __init__;
16、生成随机整数、随机小数、0-1之间的小数
import random
import numpy as np
# 生成0-1之间的随机小数
re=random.random()
# 生成区间[a,b)内的整数
re=random.randint(a,b)
# 生成n个随机小数
re=np.randn(n)
17、什么是RestFUL api
- URI,即统一资源定位符,服务器上每一种资源,比如文档、图像、视频等都由一个通用资源标识符(Uniform Resource Identifier,简称“URI”)进行定位。
- HTTP动词,常用的HTTP动词有:GET(从服务器取出资源)、 POST(服务器上新建一个资源)、 PUT(在服务器更新资源)、 PATCH(在服务器更新资源)、 DELETE(在服务器删除资源)。
- RESTful架构,服务器上每一种资源,如一个文件、一张图片、一部电影都有对应的url地址,如果客户端需要对服务器上的这个资源进行操作,就需要通过http协议执行相应的动作来操作它,比如进行获取、更新、删除等。
18、查看docker内部的日志
1、运行docker run 指令时指定日志路径
docker run \
-itd \
-p 80:80 \
--log-driver json-file \
--log-opt max-size=10m \
--log-opt max-file=3 \
--name nginx \
nginx
2、docker日志驱动
docker logs -f <容器id>
19、python内存管理机制
python有两种共存的内存管理机制:引用对象计数和垃圾回收机制
引用计数:
引用计数是一种非常高效的内存管理手段,当一个python对象被引用时其引用计数增加1,当其不再被一个变量引用时计数减1,当引用计数等于0时对象被删除
垃圾回收机制:
python中,所有能够引用其他对象的对象都被称为容器(container),只有容器之间才能形成循环引用,python的垃圾回收机制就是利用这个特点来寻找需要被释放的对象
- 首先,对于每一个容器对象,设置一个gc_refs值,并将其初始化为改对象的引用计数值;
- 对于每一个容器对象,找到其所有引用的对象,并将被引用对象的gc_refs值减1;
- 执行完步骤2之后,所有gc_refs值还大于0的对象都被非容器对象引用着,至少存在一个非循环引用,这些是不能被回收的对象,将它们放入另一个集合;如果这些对象引用着其他对象,那么那些被引用的对象也是不能被释放的,也放入另一个集合;
- 此时还剩下的对象都是无法到达的对象,可以释放这些对象了。
- 手动解循环引用:在一个对象使用结束时将其置为None,例如:
class A():
def __init__(self):
self.child=None
def destroy(self):
self.child=None
class B():
def __init__(self):
self.parent=None
def destroy(self):
self.parent=None
def test3():
a=A()
b=B()
a.child=b
b.parent=a
a.destroy()
b.destroy()
import objgraph
test3()
print(objgraph.count('A'))
print(objgraph.count('B'))
-
使用弱引用,使用python字典库weakref,例如:
def test4():
a=A()
b=B()
a.child=weakref.ref(b)
b.parent=weakref.ref(a)
import objgraph
test4()
print(objgraph.count('A'))
print(objgraph.count('B'))
20、python多线程与多进程
- CUP密集型代码(如循环计算),多进程效率更高;
- IO密集型代码(如文件操作,爬虫),多线程效率更高
GIL锁:
GIL即全局解释器锁,在同一时间内,python解释器只能运行一个线程的代码,这比较影响多线程的性能。因为在多线程的情况下,只有当线程获得了一个全局解释器锁的时候,该线程的代码才能运行,而全局锁只有一个,所以使用python多线程在同一时刻也只有一个线程在运行,因此在多核的情况下也只能发挥出单核的性能。
进程:
进程是系统分配资源的最小单位,实现方式是multiprocess。
线程:
线程是系统调度的最小单位,实现方式有threading或者继承Thread并重写run方法,更多用threading。
协程:
又称微线程,是一种用户态的轻量级线程。协程拥有自己的寄存器上下文和栈。协程调度切换时,将寄存器上下文和栈保存到其他地方,在切换回来的时候,恢复先前保存的寄存器上下文和栈。因此,协程能保留上一次调用的状态。
协程的优势是有极高的执行效率,因为子程序切换不是线程切换,没有线程切换的开销,所以和多线程相比,线程数量越多,协程的性能优势就越明显;
第二大优势是不需要多线程的锁机制。只有一个线程,不存在同时写变量冲突,在协程中控制资源不需要加锁,执行效率要高,例如:
import asyncio,os
from threading import current_thread
async def sum(a,b):
print('[%s-%s] coroutine start to do:%s + %s'%(os.getpid(),current_thread().getName(),a,b))
await asyncio.sleep(1)
r=int(a) + int(b)
print('[%s-%s] coroutine start to do:%s + %s=%s'%(os.getpid(),current_thread().getName(),a,b,r))
return r
def main(a,b,c,d):
loop=asyncio.get_event_loop()
task=asyncio.gather(sum(a,b),sum(c,d))
loop.run_until_complete(task)
r1,r2=task.result()
r=r1*r2
print('[%s-%s] coroutine start to do:%s * %s===%s'%(os.getpid(),current_thread().getName(),r1,r2,r))
loop.close()
if __name__=='__main__':
main('1','2','3','4')
21、python的设计模式有哪些
最常用的就是单例模式和工厂模式。
单例模式
保证某一个实例对象只存在一个
1、重写 __new__ 方法,定义一个实例变量,在 __new__ 方法中保证该实例只初始化一次:
def Singleton(object):
_instance=None
def __new__(cls,*args,**kwargs):
if cls._instance is None:
cls._instance=object.__new__(cls,*args,**kwargs)
return cls._instance
2、闭包定义装饰器
使用闭包的方式定义一个装饰器,将类的定义隐藏到闭包函数中:
def Singleton(cls):
_instance={}
def _singleton(*args,**kwargs):
if cls not in _instance:
_instance[cls]=cls(*args,**kwargs)
return _instance[cls]
return _singleton
# 使用上面装饰器的类,构建的实例属于单例
@Singleton
class func():
def __init__(self,arg1):
self.arg1=arg1
3、多线程情况下实现单例模式
要保证多线程情况下构建的对象是单例,需要在 __new__ 函数中加入同步锁 threading.Lock()
def Singleton():
_instance=None
_instance_lock=threading.Lock()
def __new__(cls,*args,**kwargs):
if not hasattr(Singleton,"_instance"):
with Singleton._instance_lock:
if not hasattr(Singleton,"_instance"):
Singleton._instance=object.__new__(cls)
return Singleton._instance
工厂模式
工厂模式包含简单工厂、工厂方法、抽象工厂
1、简单工厂:定义一个工厂类,创建一个静态方法,根据输入的类型,返回不同的对象。
class Apple():
def __repr__(self):
return "苹果"
class Banana():
def __repr__(self):
return "香蕉"
class SimpleFactory():
@staticmethod
def get_gruit(name):
if name == 'a':
return Apple()
elif name == 'b':
return Banana()
22、OSI七层模型和TCP/IP四层模型
23、使用gevent实现协程高并发
24、django的orm中的choices详解
choices是可选字段,比如有一个下拉表,里面有男性、女性和其他,一共三个可选项,用元组表示,那么对这个字段可用choices来定义。
choices=(
(1,'male'),
(2,'female'),
(3,'other')
)
gender=models.IntegerField(choices=choices)
25、列表[1,2,3,4,5],编辑一个函数输出[1,4,9,16,25],并取出大于10 的数,最终输出[16,25]
list1=[1,2,3,4,5]
def duble_cal(i):
return i**2
def turn_(lis):
result=map(double_cal,lis)
return [i for i in result if i > 10]
说明:考察map()函数的用法,第一个参数是函数,第二参数是list。
26、在一个坐标系上有两个矩阵,坐标分别是: 矩阵1:左上角(lx1, ly1),右下角(rx1, ry1) 矩阵2:左上角(lx2, ly2),右下角(rx2, ry2) 求两个矩阵重叠的面积。
例1:第一个矩阵(1, 7), (10, 3),第二个矩阵(2, 6), (5, 0),结果为9
例2:第一个矩阵(-3, 0), (0, -3) 第二个矩阵(2, 5), (4, 3),结果为0
例3:第一个矩阵(1, 5), (6, 2),第二个矩阵(4, 4), (7, 1),结果为 4
思路:目的在于如何找到交叉图形的宽和高:
- 水平方向上最右侧:在两个矩形的第二个点中找到x轴的最小值;
- 水平方向上最左侧:在两个矩形的第一个点中找到x轴的最大值;
- 垂直方向上最上侧:在两个矩形的第一个点中找到y轴的最小值;
- 垂直方向上最下侧:在两个矩形的第二个点中找到y轴的最大值;
def calculate_area(rec1,rec2):
l_x=max(rec1[0],rec2[0])
r_x=min(rec1[2],rec2[2])
d_y=max(rec1[3],rec2[3])
u_y=min(rec1[1],rec2[1])
length=r_x - l_x
width=u_y - d_y
if length > 0 and width > 0:
return length * width
return 0
if __name__=='__main__':
rec1=[1,7,10,3]
rec2=[2,6,5,0]
result=calculate_area(rec1,rec2)
print('result:',result)