python后端总结(django)

目录

1、TCP通信的三次握手

2、tornado框架如何处理http请求的

3、解释一下uWSGI

4、解释一下nginx

5、re正则表达式

5.1 正则表达式中match和search的区别

5.2 group 与groups的区别

5.3   符号 .* 与 .*? 的区别 

6、django的并发和多线程

7、http与tcp的关系

8、Linux日志文件中关键词查找命令

8.1 查看日志前n行

8.2 查看日志后n行

8.3 关键字搜索命令(返回含有关键字的行)

9、python中的装饰器

10、 git commit后,代码回滚操作

11、两个字典合并、两个列表合并

12、什么是lambda函数

13、迭代器与生成器

14、如何解决循环导入的问题

15、python中的 __new__ 和 __init__ 的区别

16、生成随机整数、随机小数、0-1之间的小数

17、什么是RestFUL api

18、查看docker内部的日志

1、运行docker run 指令时指定日志路径

2、docker日志驱动

19、python内存管理机制

引用计数:

垃圾回收机制:

20、python多线程与多进程

GIL锁:

进程:

线程:

协程:

21、python的设计模式有哪些

单例模式

工厂模式

22、OSI七层模型和TCP/IP四层模型

23、使用gevent实现协程高并发

24、django的orm中的choices详解

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请求的

  1. 首先获取用户请求数据;
  2. 根据用户请求URL进行路由匹配,从而使得某个方法处理具体的请求;
  3. 将处理后的数据返回给客户端;
  4. 关闭客户端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的垃圾回收机制就是利用这个特点来寻找需要被释放的对象

  1. 首先,对于每一个容器对象,设置一个gc_refs值,并将其初始化为改对象的引用计数值;
  2. 对于每一个容器对象,找到其所有引用的对象,并将被引用对象的gc_refs值减1;
  3. 执行完步骤2之后,所有gc_refs值还大于0的对象都被非容器对象引用着,至少存在一个非循环引用,这些是不能被回收的对象,将它们放入另一个集合;如果这些对象引用着其他对象,那么那些被引用的对象也是不能被释放的,也放入另一个集合;
  4. 此时还剩下的对象都是无法到达的对象,可以释放这些对象了。
  • 手动解循环引用:在一个对象使用结束时将其置为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

思路:目的在于如何找到交叉图形的宽和高:

  1. 水平方向上最右侧:在两个矩形的第二个点中找到x轴的最小值;
  2. 水平方向上最左侧:在两个矩形的第一个点中找到x轴的最大值;
  3. 垂直方向上最上侧:在两个矩形的第一个点中找到y轴的最小值;
  4. 垂直方向上最下侧:在两个矩形的第二个点中找到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)

猜你喜欢

转载自blog.csdn.net/weixin_38664232/article/details/112251020