总结2

各类型的转化

1、Int 与Str之间的转化,转化结果以及转化条件

  -str(int型)可以转化为str 该过程没有条件

  -字符串转化为数字必须全部由数字组成   才能通过int(str型)转换

2、Int 与 bool 之间如何转化,转化结果以及转化条件

  - int型转化为bool没有条件,除了0转换为False以外其他数字都转换为True  bool(int型的数)

  - int(False)= 0; int(True) = 1

3:、str 与 bool 之间如何转化,转化结果以及转化条件

  - str 转化为bool没有条件,除了''转化为False其他的字符串都转为True    bool(srr型)

  - str(True)='True'   str(False)='Flase'

4、str 与 list 能否转化?如何转化?

  -可以 str>>list  1.用split切割  2.list(str字符串)

  -可以 list>>str  1.用join+ 迭代   2. str(list列表)

能转化为False

  0,“”,{},[],(),set()

字符串与列表互转

 

l = 'ab_c'

 

print(list('ab_c'),type(list('ab_c')))

列表转字符串

使用 join

l1 = ['1','2','3']
str = ''.join(l1)
print(str,type(str))
 
li = ['alex','rain']
temp = ''.join(li)
print(temp,type(temp))

元组与列表互转

# 元组转为列表 加list
tu = (1,2,3,)
l = list(tu)
print(l,type(l))
# #
# # # 列表转为元组加tuple
li = [4,5,6]
print(tuple(li),type(tuple(li)))

 函数

函数参数传递的是什么? 引用、内存地址

#魔性的用法:默认参数尽量避免使用可变数据类型
def func(a1,a2=[]):
    a2.append(a1)
    return a2


l1 = func(1)
print(l1)       # [1,]
l2 = func(3,[])
print(l2)       # [3, ]
l3 = func(4)
print(l3)       # [1,4]

默认参数只会被执行一次:第一次调用函数时,默认参数被初始化为[],以后每次调用时都会使用已经初始化的 []。

def func(a1,a2=[]):
    a2.append(a1)
    print(a2)


func(1) # [1,]
func(3,[]) # [3,]
func(4) # [1,4]
func([])    #[1]



def func(a1,a2=[]):
    a2.append(a1)
    return a2

l1 = func(1)     # l1=[1,4]
l2 = func(3,[])  # l2=[3,]
l3 = func(4)     # l3=[1,4]
print(l2)
print(l1)
print(l3)

 三元运算及应用场景

应用场景:简化if语句
# 关于三元运算
# 结果+ if  + 条件  + else + 结果
result='gt' if 1>3 else 'lt'
print(result)       # lt

val = 'aaa' if 1 == 1 else 'bbb' 
# aaa
# 理解:如果条件为真,把if前面的值赋值给变量,否则把else后面的值赋值给变量。


lambda 表达式
应用场景:简化函数
temp = lambda x,y:x+y
print(temp(4,10))   # 14

可替代:
def foo(x,y):
    return x+y
print(foo(4,10))    # 14

闭包

#  闭包:内部的函数引用了外部函数的变量
def func(arg):
    def inner():
        return arg + 1
    return inner
v = func(111)
定义

生成器表达式,列表表达式

# 生成式

val  = [i + 100 for i in range(1,11)]
print(val)
#  [101, 102, 103, 104, 105, 106, 107, 108, 109, 110]

# 列表表达式
val = [lambda  x:x+i for i in range(10)]
# 问题1:val[4]是什么?
# print(val[4])  # <function <listcomp>.<lambda> at 0x0000000002208BF8> 函数

# 2:v = val[4](1) 是什么?
# 10


# 生成器
val = (lambda x:x+i for i in range(10))
# print(val,type(val))   生成器

 ( i % 2 for i in range(10) )
print(( i % 2 for i in range(10) ))
#  <generator object <genexpr> at 0x00000000020CEEB8> 生成器
# 在Python中,有一种自定义迭代器的方式,称为生成器(Generator)。
# 定义生成器的两种方式:
# 1.创建一个generator,只要把一个列表生成式的[]改成(),就创建了一个generator:
# generator保存的是算法,每次调用next(),就计算出下一个元素的值,直到计算到最后一个元素,
没有更多的元素时,抛出StopIteration的错误。
# 2.定义generator的另一种方法。如果一个函数定义中包含yield关键字,那么这个函数就不再是一个普通函数,
而是一个generator
View Code

常见内置函数:

- map - filter

map()函数接收两个参数,一个是函数,一个是可迭代对象,map将传入的函数依次作用到序列的每个元素,并把结果作为新的list返回。
def mul(x):
    return x*x

n=[1,2,3,4,5]
res=list(map(mul,n))
print(res)  #[1, 4, 9, 16, 25]

filter()函数接收一个函数 f 和一个list,这个函数 f 的作用是对每个元素进行判断,返回 True或 False,
filter()根据判断结果自动过滤掉不符合条件的元素,返回由符合条件元素组成的新list。
def is_odd(x):
    return x % 2 == 1

v=list(filter(is_odd, [1, 4, 6, 7, 9, 12, 17]))
print(v)  #[1, 7, 9, 17]
View Code

isinstance - type

isinstance() 函数来判断一个对象是否是一个已知的类型,类似 type()。

isinstance() 与 type() 区别:

type() 不会认为子类是一种父类类型,不考虑继承关系。

isinstance() 会认为子类是一种父类类型,考虑继承关系。

如果要判断两个类型是否相同推荐使用 isinstance()。
View Code

zip

zip() 函数用于将可迭代的对象作为参数,将对象中对应的元素打包成一个个元组,然后返回由这些元组组成的列表。
>>>a = [1,2,3]
>>> b = [4,5,6]
>>> c = [4,5,6,7,8]
>>> zipped = zip(a,b)     # 打包为元组的列表
[(1, 4), (2, 5), (3, 6)]
>>> zip(a,c)              # 元素个数与最短的列表一致
[(1, 4), (2, 5), (3, 6)]
>>> zip(*zipped)          # 与 zip 相反,可理解为解压,返回二维矩阵式
[(1, 2, 3), (4, 5, 6)]
View Code

reduce

reduce() 函数会对参数序列中元素进行累积。

用传给reduce中的函数 function(有两个参数)先对集合中的第1、2个元素进行操作,得到的结果再与第三个数据用function函数运算,最后得到一个结果。

>>>def add(x, y) :            # 两数相加
...     return x + y
... 
>>> reduce(add, [1,2,3,4,5])   # 计算列表和:1+2+3+4+5
>>> reduce(lambda x, y: x+y, [1,2,3,4,5])  # 使用 lambda 匿名函数
View Code

生成器、迭代器、装饰器、可迭代对象

生成器

包括含有yield这个关键字,生成器也是迭代器,调动next把函数变成迭代器。

应用场景:
range/xrange
    - py2: range(1000000)  ,会立即创建,xrange(1000000)生成器
    - py3:range(10000000)生成器 

- redis获取值
  

- redis获取值
conn = Redis(...)

    def hscan_iter(self, name, match=None, count=None):
      """
      Make an iterator using the HSCAN command so that the client doesn't
      need to remember the cursor position.

      ``match`` allows for filtering the keys by pattern

      ``count`` allows for hint the minimum number of returns
      """
      cursor = '0'
      while cursor != 0:
        # 去redis中获取数据:12
        # cursor,下一次取的位置
        # data:本地获取的12条数数据
        cursor, data = self.hscan(name, cursor=cursor,match=match, count=count)
        for item in data.items():
          yield item

stark组件

def index(request):
    data = [
      {'k1':1,'name':'alex'},
      {'k1':2,'name':'老男孩'},
      {'k1':3,'name':'小男孩'},
    ]
    new_data = []
    for item in data:
      item['email'] = "xxx@qq.com"
      new_data.append(item)

    return render(request,'xx.html',{'data':new_data})
                                                  new_data.append(item)
    return render(request,'xx.html',{'data':new_data})
应用场景

迭代器

含有__iter__和__next__方法 (包含__next__方法的可迭代对象就是迭代器)

 特点:
      访问者不需要关心迭代器内部的结构,仅需通过next()方法不断去取下一个内容(惰性计算)
      不能随机访问集合中的某个值 ,只能从头到尾依次访问
      访问到一半时不能往回退
      便于循环比较大的数据集合,节省内存
View Code

可迭代对象

内部实现__iter__方法且返回一个迭代器。

应用场景: 
    - wtforms中对form对象进行循环时候,显示form中包含的所有字段。
        class LoginForm(Form):
            name = simple.StringField(
                label='用户名',
                validators=[
                    validators.DataRequired(message='用户名不能为空.'),
                    validators.Length(min=6, max=18, message='用户名长度必须大于%(min)d且小于%(max)d')
                ],
                widget=widgets.TextInput(),
                render_kw={'class': 'form-control'}
            )
            pwd = simple.PasswordField(
                label='密码',
                validators=[
                    validators.DataRequired(message='密码不能为空.'),
                    validators.Length(min=8, message='用户名长度必须大于%(min)d'),
                    validators.Regexp(regex="^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[$@$!%*?&])[A-Za-z\d$@$!%*?&]{8,}",
                                      message='密码至少8个字符,至少1个大写字母,1个小写字母,1个数字和1个特殊字符')

                ],
                widget=widgets.PasswordInput(),
                render_kw={'class': 'form-control'}
            )

        
        form = LoginForm()
        for item in form:
            print(item)
            
    - 列表、字典、元组
应用场景

装饰器

调用装饰器其实是一个闭包函数,为其他函数添加附加功能,不修改被修改的源代码和不修改被修饰的方式,装饰器的返回值也是一个函数对象。
比如:插入日志、性能测试、事物处理、缓存、权限验证等,有了装饰器,就可以抽离出大量与函数功能本身无关的雷同代码并继续重用。

问题:什么是装饰器?
  在对原函数不进行修改时,在函数执行前和执行后添加功能

问题:手写装饰器 
import functools

def warpper(func):
    @functools.wraps(func)   #不改变原函数属性
    def inner(*args,**kwargs):
        #执行函数前
        return func(*args,**kwargs)
        #执行函数后
    return inner

# 1. 执行wapper函数,并将被装饰的函数当做参数。 wapper(index)
# 2. 将第一步的返回值,重新赋值给  新index =  wapper(老index)
@warpper   #index=warpper(index)
def index(x):
    return x+100
问题:应用场景
    django: csrf 内置认证、缓存
    flask: 路由、before_request

带参数装饰器:flask:路由
       CBV as_view()
场景

偏函数

偏函数:
import functools
def func(a1, a2, a3):
    return a1 + a2 + a3

new_func = functools.partial(func, 11, 2)  #将11,2依次传入到func函数的前两个参数
print(new_func(3))

应用场景
falsk中取值时 通过localproxy 、偏函数、localstack、local
View Code

谈谈面向对象认识

-继承、封装、多态(简单描述)

python中一切皆对象,函数也是对象,类也是对象。
封装,对数据的封装,对对象的封装。
继承,一般用于在一个已有的设计良好的类基础上进行二次开发,通过内置函数super()或者“基类名.方法名()”的方式实现这一目的。
多态:是指基类的同一方法在不同的派生类对象中具有不同的表现和行为。
-双下划线:

    __mro__ wtform中 FormMeta中继承类的优先级

     __dict__    

    __new__ ,实例化但是没有给当前对象
        wtforms,字段实例化时返回:不是StringField,而是UnboundField
       rest frawork many=Turn  中的序列化

    __call__
       flask 请求的入口app.run()
        字段生成标签时:字段.__str__ => 字段.__call__ => 插件.__call__

       __iter__ 循环对象是,自定义__iter__
        wtforms中BaseForm中循环所有字段时定义了__iter__

-metaclass
    - 作用:用于指定当前类使用哪个类来创建
    - 场景:在类创建之前定制操作
    示例:wtforms中,对字段进行排序。
View Code

深度优先及广度优先

Python的类可以继承多个类,Python的类如果继承了多个类,那么其寻找方法的方式有两种

当类是经典类时,多继承情况下,会按照深度优先方式查找

当类是新式类时,多继承情况下,会按照广度优先方式查找

简单点说就是:经典类是纵向查找,新式类是横向查找

经典类和新式类的区别就是,在声明类的时候,新式类需要加上object关键字。在python3中默认全是新式类
View Code

给一个路径,找到该路径下所有文件

# (使用os.walk)

file-- 是你所要便利的目录的地址, 返回的是一个三元组(root,dirs,files)。

root 所指的是当前正在遍历的这个文件夹的本身的地址
dirs 是一个 list ,内容是该文件夹中所有的目录的名字(不包括子目录)
files 同样是 list , 内容是该文件夹中所有的文件(不包括子目录)

def open_2(file):

    for root, dirs , files in os.walk(file):
        print("ss",files)
        for filename in files:
            print(os.path.abspath(os.path.join(root, filename)))

open_2("F:\搜索")
View Code
# 方法一:(面试要求不使用os.walk)
def print_directory_contents(sPath):
    import os

    for sChild in os.listdir(sPath):
        sChildPath = os.path.join(sPath, sChild)
        if os.path.isdir(sChildPath):
            print_directory_contents(sChildPath)
        else:
            print(sChildPath)
            
# 方法二:(使用os.walk)
def print_directory_contents(sPath):
    import os
    for root, _, filenames in os.walk(sPath):
        for filename in filenames:
            print(os.path.abspath(os.path.join(root, filename)))


print_directory_contents('.')
2中方法

创建、删除文件

1 # 创建一个文件
2 open("chao.txt","w",encoding="utf-8")
3 import os
  #删除文件
4 os.remove("chao.txt")

第三方软件安装

 1、pip3 包管理器

 2、源码安装

    -下载、解压

    -python  setup.py  bulid

    -python  setup.py  install
2种

 

网络编程

C/S架构

C/S:Client(客户端)与Server(服务端)即:客户端与服务端架构,这种架构也是从用户层面(也可以是物理层面)来划分的。

这里的客户端一般泛指应用程序的.exe,程序需要先安装后,才能运行在用户的电脑上,对用户的电脑操作系统环境依赖较大。

B/S架构

指的是Brosver端(浏览器端)与Sever端,浏览器端与服务器端架构,这种架构是从用户层面来划分的。

隶属与C/S架构的

Browser浏览器,其实也是一种Client客户端,只需在浏览器上通过HTTP请求服务器端相关的资源(网页资源),客户端Browser浏览器就能进行增删改查。

优点:统一了所有应用程序的入口、方便、轻量级

MAC地址

mac地址:在网卡上的mac地址是网卡的生产厂商给定的,每生产一个网卡,就要附加一个mac地址(全球唯一)

IP地址

一个4位点分十进制组成的

如何通过ip地址确定Mac地址

arp协议:把ip地址转换为mac地址

两个电脑是如何传递信息

信息过来 ----> 路由器-----> 路由表 ---> 交换机 ---> 交换机通过广播找所属ip的电脑 ----> 所属ip的电脑通过单播的形式回答

路由表:里面有网关ip的网段信息

交换机与路由器区别

1:交换机:是负责内网里面的数据传递(arp协议)根据MAC地址寻址

路由器:在网络层,路由器根据路由表,寻找该ip的网段

2:路由器可以处理TCP/IP协议

3:路由器可以把一个IP分配给很多个主机使用,这些主机对外只表现出一个IP。

交换机可以把很多主机连起来,这些主机对外各有各的IP。

4:交换机是做端口扩展的,也就是让局域网可以连进来更多的电脑。

路由器是用来做网络连接,也就是;连接不同的网络

如何确定某个主机上某个程序

ip+端口  确定某个主机上的某个程序

端口:每个电脑上所运行的程序都有唯一(本机)的一个端口

&&---> 按位与

ip地址  && 子网掩码   ==== >  可以确定一个网关ip

目标: 155.155.12.12  msg  166.166.12.12
过程:(ip地址是随时变化的)
155.155.12.12  msg  1的ip
1的ip          msg   2的ip
2的ip          msg     166.166.12.12

 

 

-OSI 7层协议

首先,用户感知到的只是最上面一层应用层,自上而下每层都依赖于下一层,所以我们从最下一层开始切入,比较好理解

每层都运行特定的协议,越往上越靠近用户,越往下越靠近硬件

物理层:主要定义物理设备标准,基于电器特性发送高低电压(电信号),高电压对应数字1,低电压对应数字0

数据链路层:单纯的电信号0和1没有意义,需要定义了电信号的分组方式及每组的意思。

网络层:给位于不同地理位置的网络中的两个主机系统之间提供连接和路径选择,就是提供一些网络地址来区分不同的广播域/或子网

传输层:建立端口到端口的通信(区分每一个电脑的应用程序的每个端口)

应用层:用户使用的都是应用程序,应用程序大都不一样,所以应用层是规定应用程序的数据格式

- 三次握手、四次挥手

  三次握手:

    SYC=1(建立连接)  ACK(确认请求)

    1、客户端(Client)向服务端(Server)发一次请求(SYN=1,随机产生一个值seq=J)

    2、服务端确认并回复客户端(ACK=1, SYC=1,并在seq基础上产生一个随机数发给客户端)

    3、客户端检验确认请求(ACK=1)     此时客户端与服务端就建立了连接

  四次挥手:

    FAN=1(断连接) ACK=1(确认请求)

    1、客户端向服务端发一次请求(FAN=1)

    2、服务端回复客户端 (ACK=1)  (断开客户端—>服务端)

    3、服务端再向客户端发请求(FAN=1)  (因为有数据传输,所以2、3不能合并)

    4、客户端确认请求(ACK=1)        (断开服务端--->客户端)
View Code

- TCP和UDP

TCP与UDP基本区别
  1.基于连接与无连接
  2.TCP要求系统资源较多,UDP较少; 
  3.UDP程序结构较简单 
  4.流模式(TCP)与数据报模式(UDP); 
  5.TCP保证数据正确性,UDP可能丢包 
  6.TCP保证数据顺序,UDP不保证 
  
UDP应用场景:
  1.面向数据报方式
  2.网络数据大多为短消息 
  3.拥有大量Client
  4.对数据安全性无特殊要求
  5.网络负担非常重,但对响应速度要求高
 
具体编程时的区别
   1.socket()的参数不同 
   2.UDP Server不需要调用listen和accept 
   3.UDP收发数据用sendto/recvfrom函数 
   4.TCP:地址信息在connect/accept时确定 
   5.UDP:在sendto/recvfrom函数中每次均 需指定地址信息 
   6.UDP:shutdown函数无效
 
编程区别
   通常我们在说到网络编程时默认是指TCP编程,即用前面提到的socket函数创建一个socket用于TCP通讯,函数参数我们通常填为SOCK_STREAM。即socket(PF_INET, SOCK_STREAM, 0),这表示建立一个socket用于流式网络通讯。 
   SOCK_STREAM这种的特点是面向连接的,即每次收发数据之前必须通过connect建立连接,也是双向的,即任何一方都可以收发数据,协议本身提供了一些保障机制保证它是可靠的、有序的,即每个包按照发送的顺序到达接收方。 

  而SOCK_DGRAM这种是User Datagram Protocol协议的网络通讯,它是无连接的,不可靠的,因为通讯双方发送数据后不知道对方是否已经收到数据,是否正常收到数据。任何一方建立一个socket以后就可以用sendto发送数据,也可以用recvfrom接收数据。根本不关心对方是否存在,是否发送了数据。它的特点是通讯速度比较快。大家都知道TCP是要经过三次握手的,而UDP没有。 

基于上述不同,UDP和TCP编程步骤也有些不同,如下:

TCP: 
TCP编程的服务器端一般步骤是: 
  1、创建一个socket,用函数socket(); 
  2、设置socket属性,用函数setsockopt(); * 可选 
  3、绑定IP地址、端口等信息到socket上,用函数bind(); 
  4、开启监听,用函数listen(); 
  5、接收客户端上来的连接,用函数accept(); 
  6、收发数据,用函数send()和recv(),或者read()和write(); 
  7、关闭网络连接; 
  8、关闭监听; 

TCP编程的客户端一般步骤是: 
  1、创建一个socket,用函数socket(); 
  2、设置socket属性,用函数setsockopt();* 可选 
  3、绑定IP地址、端口等信息到socket上,用函数bind();* 可选 
  4、设置要连接的对方的IP地址和端口等属性; 
  5、连接服务器,用函数connect(); 
  6、收发数据,用函数send()和recv(),或者read()和write(); 
  7、关闭网络连接;

UDP:
与之对应的UDP编程步骤要简单许多,分别如下: 
  UDP编程的服务器端一般步骤是: 
  1、创建一个socket,用函数socket(); 
  2、设置socket属性,用函数setsockopt();* 可选 
  3、绑定IP地址、端口等信息到socket上,用函数bind(); 
  4、循环接收数据,用函数recvfrom(); 
  5、关闭网络连接; 

UDP编程的客户端一般步骤是: 
  1、创建一个socket,用函数socket(); 
  2、设置socket属性,用函数setsockopt();* 可选 
  3、绑定IP地址、端口等信息到socket上,用函数bind();* 可选 
  4、设置对方的IP地址和端口等属性; 
  5、发送数据,用函数sendto(); 
  6、关闭网络连接;

TCP和UDP是OSI模型中的运输层中的协议。TCP提供可靠的通信传输,而UDP则常被用于让广播和细节控制交给应用的通信传输。

UDP补充:
   UDP不提供复杂的控制机制,利用IP提供面向无连接的通信服务。并且它是将应用程序发来的数据在收到的那一刻,立刻按照原样发送到网络上的一种机制。即使是出现网络拥堵的情况下,UDP也无法进行流量控制等避免网络拥塞的行为。此外,传输途中如果出现了丢包,UDO也不负责重发。甚至当出现包的到达顺序乱掉时也没有纠正的功能。如果需要这些细节控制,那么不得不交给由采用UDO的应用程序去处理。换句话说,UDP将部分控制转移到应用程序去处理,自己却只提供作为传输层协议的最基本功能。UDP有点类似于用户说什么听什么的机制,但是需要用户充分考虑好上层协议类型并制作相应的应用程序。

TCP补充:
  TCP充分实现了数据传输时各种控制功能,可以进行丢包的重发控制,还可以对次序乱掉的分包进行顺序控制。而这些在UDP中都没有。此外,TCP作为一种面向有连接的协议,只有在确认通信对端存在时才会发送数据,从而可以控制通信流量的浪费。TCP通过检验和、序列号、确认应答、重发控制、连接管理以及窗口控制等机制实现可靠性传输。


TCP与UDP区别总结:
1、TCP面向连接(如打电话要先拨号建立连接);UDP是无连接的,即发送数据之前不需要建立连接
2、TCP提供可靠的服务。也就是说,通过TCP连接传送的数据,无差错,不丢失,不重复,且按序到达;UDP尽最大努力交付,即不保   证可靠交付
3、TCP面向字节流,实际上是TCP把数据看成一连串无结构的字节流;UDP是面向报文的
  UDP没有拥塞控制,因此网络出现拥塞不会使源主机的发送速率降低(对实时应用很有用,如IP电话,实时视频会议等)
4、每一条TCP连接只能是点到点的;UDP支持一对一,一对多,多对一和多对多的交互通信
5、TCP首部开销20字节;UDP的首部开销小,只有8个字节
6、TCP的逻辑通信信道是全双工的可靠信道,UDP则是不可靠信道

区别

 

TCP/UDP区别
 TCP协议是面向连接,保证高可靠性(数据无丢失,数据无失序,数据无错误,数据无重复达到)传输层协议
 UDP:数据丢失,无秩序的传输层协议(qq基于udp协议)

 并发编程

- 进程、线程、协程区别

 一、概念

  1、进程

进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位。每个进程都有自己的独立内存空间,不同进程通过进程间通信来通信。由于进程比较重量,占据独立的内存,所以上下文进程间的切换开销(栈、寄存器、虚拟内存、文件句柄等)比较大,但相对比较稳定安全。

  2、线程

线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位.线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源。线程间通信主要通过共享内存,上下文切换很快,资源开销较少,但相比进程不够稳定容易丢失数据。

  3、协程

协程是一种用户态的轻量级线程,协程的调度完全由用户控制。协程拥有自己的寄存器上下文和栈。协程调度切换时,将寄存器上下文和栈保存到其他地方,在切回来的时候,恢复先前保存的寄存器上下文和栈,直接操作栈则基本没有内核切换的开销,可以不加锁的访问全局变量,所以上下文的切换非常快。

 

二、区别:

  1、进程多与线程比较

线程是指进程内的一个执行单元,也是进程内的可调度实体。线程与进程的区别:
1) 地址空间:线程是进程内的一个执行单元,进程内至少有一个线程,它们共享进程的地址空间,而进程有自己独立的地址空间
2) 资源拥有:进程是资源分配和拥有的单位,同一个进程内的线程共享进程的资源
3) 线程是处理器调度的基本单位,但进程不是
4) 二者均可并发执行

5) 每个独立的线程有一个程序运行的入口、顺序执行序列和程序的出口,但是线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制

  2、协程多与线程进行比较

1) 一个线程可以多个协程,一个进程也可以单独拥有多个协程,这样python中则能使用多核CPU。

2) 线程进程都是同步机制,而协程则是异步

3) 协程能保留上一次调用时的状态,每次过程重入时,就相当于进入上一次调用的状态

 

 三、进程和线程、协程在python中的使用

  1、多进程一般使用multiprocessing库,来利用多核CPU,主要是用在CPU密集型的程序上,当然生产者消费者这种也可以使用。多进程的优势就是一个子进程崩溃并不会影响其他子进程和主进程的运行,但缺点就是不能一次性启动太多进程,会严重影响系统的资源调度,特别是CPU使用率和负载。使用多进程可以查看文章《python 多进程使用总结》。注:python2的进程池在类中的使用会有问题,需要把类函数定义成全局函数。

  2、多线程一般是使用threading库,完成一些IO密集型并发操作。多线程的优势是切换快,资源消耗低,但一个线程挂掉则会影响到所有线程,所以不够稳定。现实中使用线程池的场景会比较多,具体可参考《python线程池实现》。

  3、协程一般是使用gevent库,当然这个库用起来比较麻烦,所以使用的并不是很多。相反,协程在tornado的运用就多得多了,使用协程让tornado做到单线程异步,据说还能解决C10K的问题。所以协程使用的地方最多的是在web应用上。

总结一下就是IO密集型一般使用多线程或者多进程,CPU密集型一般使用多进程,强调非阻塞异步并发的一般都是使用协程,当然有时候也是需要多进程线程池结合的,或者是其他组合方式。
View Code

 - GIL锁

GIL:全局解释器锁,是锁在cpython解释器上,导致同一时刻,同一进程只能有一个线程被执行
多进程:多进程模块multiprocessing来实现,cpu密集型,IO计算型可以用多进程
多线程:多线程模块threading来实现,IO密集型,多线程可以提高效率
协程:依赖于geenlet,对于多线程应用。cpu通过切片的方式来切换线程间的执行,遇到IO操作自动切换,线程切换时需要耗时,
而协成好处没有切换的消耗,没有锁定概念。 进程:是资源管理单位,进行是相互独立的,实现并发和并发 线程:是最小的执行单位,线程的出现为了降低上下文切换的消耗,提供系统的并发性

- 进程池和线程池

数据库

http://www.cnblogs.com/wupeiqi/articles/5748496.html

引擎

inondb、myisam

加锁语句关键点:for update

名称 特点
inondb 支持事务操作
 

加锁语句:

  表锁:select * from tb for update

  行锁:select id,name from tb where id=2 for update

   
myisam 查询速度快
  全文索引
 

加锁语句

  表锁:select * from tb for update

设计表

            - FK
            - M2M

什么时候设计FK?

下拉框里面的数据就需要用FK关联另一张表

M2M?

多选的下拉框,或者checkbox
            PS:
                - 数据库部分:课程、老师、学生、分数
                - 博客
                - 权限表

查询

http://www.cnblogs.com/wupeiqi/articles/5729934.html

  group by 分组对聚合的条件进行筛选需要通过havhing

SQL的left join 、right join、inner join之间的区别

left join (左连接) 返回包括左表中的所有记录和右表中联结字段相等的记录
right join(右连接) 返回包括右表中的所有记录1和左表中联结字段相等的记录
inner join(内连接): 只返回两个表中联结字段相等的行

概念

触发器

对数据库某个表的增加,删除,改前后定义一些操作

触发器是一种与表操作有关的数据库对象,当触发器所在表上发生指定事件时,将调用该对象, 即表的操作事件触发表上的触发器的执行。 可以这样来理解:当某个表上的数据发生变化的时候,就会执行一个函数,这个函数可能会去 执行一些对其他表的操作。这个函数就是触发器,它就相当于编程里的监听器一样,一旦监听到这个表 发生了某些变化,就会执行已经写好的一套逻辑。按照面向对象的思想,这个触发器是该表的一个属性

函数

触发函数是通过select

在SQL语句中使用函数

-select sleep(5)

- select date_format(ctime,"%m") from tb;

聚合函数
    max/sum/min/avg


时间格式化
    date_format
select date_format('2009-10-01 22:30:10','%W %M %Y')
# Thursday October 2009

select date_format('2014-10-10 22:24:10','%H:%i:%s')
# 22:24:10

select date_format('1996-12-20 22:24:10','%D %y %a %d %m %b %j')
# 20th 96 Fri 20 12 Dec 355

select date_format('1996-12-20', '%x %v')
# 1996 51

select date_format('2006-06-00','%d')
# 00

字符串拼接 concat
例子:
mysql> select concat('lishi','haha','天天向上');
+---------------------------------------+
| concat('lishi','haha','天天向上')     |
+---------------------------------------+
| lishihaha天天向上                     |
+---------------------------------------+
1 row in set (0.00 sec)

注意:当拼接了null,则返回null
mysql> select concat('lishi','haha','天天向上');
+---------------------------------------+
| concat('lishi','haha','天天向上')     |
+---------------------------------------+


补充
# 截取字符串
select substring('zxcvbnm',5)
# bnm

select substring('zxcvbnm',-5,3)
# cvb

# 返回的是字符类型个数
select char_length('中文')
# 2

# 返回的是字节个数
select length('中文')
# 6
| lishihaha天天向上                     |
+---------------------------------------+
1 row in set (0.00 sec)
关于mysql的函数

存储过程

含义:将SQL语句保存到数据库中,并命名;以后在代码中调用时,直接发送名称
参数类型:
  in 只将参数传进去
  out 只拿结果
  inout 既可以传,可以取
通过代码调用:
import pymysql conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', passwd='123', db='t1')
cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
# 执行存储过程
cursor.callproc('p1', args=(1, 22, 3, 4))
# 获取执行完存储的参数
cursor.execute("select @_p1_0,@_p1_1,@_p1_2,@_p1_3")
result = cursor.fetchall()
conn.commit()
cursor.close()
conn.close()
print(result)



用途:保险、金融、 存储过程包含了一系列可执行的sql语句,存储过程存放于数据库(MySQL)中,通过调用它的名字可以执行其内部的一堆sql。 使用存储过程的优点:   (
1)用于替代程序写的SQL语句,实现程序与sql解耦;   (2)基于网络传输,传别名的数据量小,而直接传sql数据量大。缺点是程序员扩展功能不方便。

问题

存储过程和函数的区别

存储过程  函数
返回值:return 返回值:out/inout

参数:in/out/in out

参数:in/out/in out
   

 

视图

对某些表进行sql查询,将结果显示实时显示出来(只能查)

视图是一个虚拟表(非真实存在),其本质是【根据SQL语句获取动态的数据集,并为其命名】,用户使用时只需使用【名称】即可获取结果集,并可以将其当作表来使用

注意

使用视图时,将其当作表进行操作即可,由于视图是虚拟表,所以无法使用其对真实表进行创建、更新和删除操作,仅能做查询用。

v = select * from tb where id < 1000
这里的v相当于视图
select * from v
select * from (select * from tb where id < 1000) as v

以上内容都是保存在数据库中。

索引

特殊的存储数据结构:B+树/哈希索引(类似字典的key,当数据都不重复可以做哈希索引) ==> 查找速度快;
缺点:建索引需要额外的文件存在索引,所以更新速度慢(修改)
b+树性质
  1、索引字段要尽量的小
  2、索引的最左匹配原则
单列
功能
   普通索引:加速查找
   唯一索引:加速查找 + 约束:不能重复(只能有一个空,不然就重复了)
   主键(primay key):加速查找 + 约束:不能重复 +  不能为空
多列
  联合索引(多个列创建索引)-----> 相当于单列的普通索引
  联合唯一索引            -----> 相当于单列的唯一索引
  ps:联合索引的特点:遵循最左前缀的规则
其他词语:
·· - 索引合并,利用多个单例索引查询;(例如在数据库查用户名和密码,分别给用户名和密码建立索引)
   - 覆盖索引,在索引表中就能将想要的数据查询到;

问题:创建了索引,应该如何命中索引或什么情况无法命中索引**

- like '%xx'
    select * from tb1 where name like '%cn';
- 使用函数
    select * from tb1 where reverse(name) = 'wupeiqi';
- or
    select * from tb1 where nid = 1 or email = 'seven@live.com';
    特别的:当or条件中有未建立索引的列才失效,以下会走索引
            select * from tb1 where nid = 1 or name = 'seven';
            select * from tb1 where nid = 1 or email = 'seven@live.com' and name = 'alex'
- 类型不一致
    如果列是字符串类型,传入条件是必须用引号引起来,不然...
    select * from tb1 where name = 999;
- !=
    select * from tb1 where name != 'alex'
    特别的:如果是主键,则还是会走索引
        select * from tb1 where nid != 123
- >
    select * from tb1 where name > 'alex'
    特别的:如果是主键或索引是整数类型,则还是会走索引
        select * from tb1 where nid > 123
        select * from tb1 where num > 123
- order by
    select email from tb1 order by name desc;
    当根据索引排序时候,选择的映射如果不是索引,则不走索引
    特别的:如果对主键排序,则还是走索引:
        select * from tb1 order by nid desc;
 
- 组合索引最左前缀
    如果组合索引为:(name,email)
    name and email       -- 使用索引
    name                 -- 使用索引
    email                -- 不使用索引
无法命中索引

如何开启慢日志查询?

修改配置文件
slow_query_log = OFF                            是否开启慢日志记录
long_query_time = 2                              时间限制,超过此时间,则记录
slow_query_log_file = /usr/slow.log        日志文件
log_queries_not_using_indexes = OFF     为使用索引的搜索是否记录

下面是开启
slow_query_log = ON
long_query_time = 2   
log_queries_not_using_indexes = OFF 
log_queries_not_using_indexes = ON
配置文件
注:查看当前配置信息:
       show variables like '%query%'
     修改当前配置:
    set global 变量名 = 值
相关

执行计划

查看有没有命中索引,让数据库帮看看运行速度快不快
explain select * from table;

当type为all时,是为全表索引

导入、导出数据库

导出现有数据库数据:(当有提示出入密码。-p就不用加密码)
  mysqldump -u用户名 -p密码 数据库名称 >导出文件路径           # 结构+数据
  mysqldump -u用户名 -p密码 -d 数据库名称 >导出文件    路径       # 结构 

导入现有数据库数据:
    mysqldump -uroot -p密码  数据库名称 < 文件路径  
命令

数据库分页

通过limit offset做分页
select * from tb limit 3 offset 5
ORM:
modes.User.objects。all()【0:10】 本质也是执行上面

这种分页的问题:
select * from tb limit 3 offset 5
当分页页码很多时,翻到越后面就速度就越慢,当点击第一页就变快了
当点击1000页时,他还是从最前面一行一行找到后面(扫描的数据会越来越多)

解决:
答案1:(错误)
先查主键,在分页
例如:
先找id --> 覆盖索引 速度只是稍微快一点
select * from tb where id in (select id from tb where limit 10 offset 30)

答案2: 
按照业务需求是否可以设置只让用户看200页
例如:博客园只能看200页、抽屉网

答案3:
记录当前页  数据ID最大值和最小值
在翻页时,根据条件先进行筛选;筛选完毕之后,再根据limit offset 查询。
select * from (select * from tb where id > 22222222) as B limit 10 offset 0

如果用户自己修改页码,也可能导致慢;此时对url种的页码进行加密(rest framework )。
分页问题

优化方案

1、创建数据表时把固定长度的放在前面()
2、将固定数据放入内存: 例如:choice字段 (django中有用到,数字1、23…… 对应相应内容)
3char 和 varchar 的区别(char可变, varchar不可变 )
  
4、联合索引遵循最左前缀(从最左侧开始检索) 5、避免使用 select * 6、读写分离     - 实现:两台服务器同步数据     - 利用数据库的主从分离:主,用于删除、修改、更新;从,用于查;
读写分离:利用数据库的主从进行分离:主,用于删除、修改更新;从,用于查
7、分库
    - 当数据库中的表太多,将某些表分到不同的数据库,例如:1W张表时
    - 代价:连表查询
8、分表
    - 水平分表:将某些列拆分到另外一张表,例如:博客+博客详情
    - 垂直分表:讲些历史信息分到另外一张表中,例如:支付宝账单

9、加缓存
    - 利用redis、memcache (常用数据放到缓存里,提高取数据速度)
如果只想获取一条数据 - select * from tb where name=‘alex’ limit 1

爬虫

并发的方案

  多进程---> 多线程 ----> 利用“异步非阻塞”模块实现单线程并发请求

本质:
    sk = socket()
    # 阻塞
    sk.connect(('www.cnblogs.com',80))
    sk.sendall(b"GET /wupeiqi http1.1\r\n.....\r\n\r\n")
    sk.sendall(b"POST /wupeiqi http1.1\r\n.....\r\n\r\nuser=alex&pwd=123")
    # 阻塞
    data = sk.recv(8096)
    sk.close()

什么是异步非阻塞

  非阻塞:不等待

代码:
sk = socket.socket()
sk.setblocking(False)

  异步:回调,当达到某个指定的状态之后,自动调用特定函数

  实例:

  nb_async.py   实现异步非阻塞的模块

IO多路复用

  监听多个socket是否发生变化

如何自定义异步非阻塞模块?

基于socket设置setblocking和 IO多路复用来实现。
爬虫发送Http请求本质创建socket对象;
IO多路复用"循环"监听socket是否发生变化,一旦发生变化,我们可以自定义操作(触发某个函数的执行)

Http协议本质

  浏览器本质,socket客户端遵循Http协议
  HTTP协议本质:通过\r\n分割的规范+ 请求响应之后断开链接   ==  >  无状态、 短连接
具体:
  Http协议是建立在tcp之上的,是一种规范,它规范定了发送的数据的数据格式,
然而这个数据格式是通过\r\n 进行分割的,请求头与请求体也是通过2个\r\n分割的,响应的时候,
响应头与响应体也是通过\r\n分割,并且还规定已请求已响应就会断开链接 即
---> 短连接、无状态

Django rest framework 框架

restful

1:用django写接口时,有没有用什么框架?

- 使用rest framework框架
- 原生CBV

2:rest framework框架优点?

rest framework帮助开发者提供了很多组件,可以提高开发效率。

3:有哪些组件

- 路由  
    自动帮助开发者快速为一个视图创建4个url     www.oldboyedu.com
/api/v1/student/www.oldboyedu.com/api/v1/student(?P<format>\w+)     www.oldboyedu.com/api/v1/student/(?P<pk>\d+)/www.oldboyedu.com/api/v1/student/(?P<pk>\d+)(?P<format>\w+) - 版本处理 - 问题:版本都可以放在那里?   - url   - GET   - 请求头 - 认证   - 问题:认证流程? - 权限   - 权限是否可以放在中间件中?以及为什么? - 节流   - 匿名用户可以真正的防止?无法做到真正的访问频率控制,只能把小白拒之门外。   如果要封IP,使用防火墙来做。   - 登录用户可以通过用户名作为唯一标示进行控制,如果有人注册很多账号,也无法防止。 - 视图 - 解析器 ,根据Content-Type请求头对请求体中的数据格式进行处理。request.data - 分页 - 序列化   - 序列化     - source     - 定义方法   - 请求数据格式校验 - 渲染器

4:谈谈对restful规范的理解

1:restful其实是一套编写接口的规范、协议。规定了如何编写以及如何设置返回值,状态码等信息。

2:最显著的特点
    restful:(用restful)
    给用户一个url,根据method不同在后端做不同的处理,比如:post创建数据、get获取数据
、put和patch修改数据、delete删除数据。 no rest:(不用restful) 给调用者很对url,每个url代表一个功能,比如;add_usser
/delete_user/edit_user 3: 当然,协议里面还有其他的,比如:、 - 版本 ,来控制让程序有多个版本共存的情况,版本可以放在url、请求头(accept/自定义)、GET参数 - 状态码 ,200(成功,服务器提供了请求的网页)/300( 重定向 - 要完成请求必须进行更进一步的处理)
        /400(客户端错误)/500(服务端错误) - url中尽量使用名词,restful也可以称为“面向资源编程” - api标示: api.baidu.com www.baidu.com/api/

5:restful怎么学的

- 因为之前公司要写这样项目
    - 接口
    - 公司要做前后端分离的项目
    - 公司要做微信小程序的开发
- 所以就开始学习restful规范,看的技术文章 阮一峰的博客学到的规范。

6:状态码

1XX  提示信息 - 表示请求已被成功接收,继续处理

2XX  成功 - 表示请求已被成功接收,理解,接受

3XX  重定向 - 要完成请求必须进行更进一步的处理

4XX  客户端错误 -  请求有语法错误或请求无法实现

5XX  服务器端错误 -   服务器未能实现合法的请求 

7:method

post 创建数据、get获取数据、put和patch修改数据、delete删除数据

8:常见请求头

- user-agent
- host
- referer
- cookie 
- content-type 

 9:你是用什么开发的restful接口?

  使用django rest framework框架

10:为什么使用django rest framework框架

在编写接口时,可以不使用django rest framework框架

如果不使用:也是可以做,那么就可以django的CBV来实现,开发者编写的代码会更多一些。

使用:内部帮助我们提供了很多方便的组件,我们通过配置就可以完成相应操作,如:
序列化,可以做用户请求数据校验+queryset对象的序列化称为json
解析器,获取用户请求数据request.data,会自动根据content-type请求头的能对数据进行解析
分页:将从数据库获取到的数据在页面进行分页显示。
还有其他:
    认证
    权限
    访问频率控制

11:rest framwork视图你都用过哪些基类?(继承哪些类)

a. 继承APIView(最原始)但定制性比较强
    这个类属于rest framework中的顶层类,内部帮助我们实现了只是基本功能:认证、权限、频率控制,
但凡是数据库、分页等操作都需要手动去完成,比较原始。
class GenericAPIView(APIView) def post(...): pass
b.继承GenericViewSet(ViewSetMixin,generics.GenericAPIView)
  首先他的路由就发生变化 如果继承它之后,路由中的as_view需要填写对应关系
  在内部也帮助我们提供了一些方便的方法:
  get_queryset
  get_object
  get_serializer
  get_serializer_class
  get_serializer_context
  filter_queryset
注意:要设置queryset字段,否则会抛出断言的异常。

代码
只提供增加功能 只继承
GenericViewSet

class TestView(GenericViewSet):
  serialazer_class = xxx
  def creat(self,*args,**kwargs):
    pass  # 获取数据并对数据

c. 继承 modelviewset --> 快速快发
    -ModelViewSet(增删改查全有+数据库操作)
    -mixins.CreateModelMixin(只有增),GenericViewSet
    -mixins.CreateModelMixin,DestroyModelMixin,GenericViewSet
  对数据库和分页等操作不用我们在编写,只需要继承相关类即可。
  

示例:只提供增加功能
class TestView(mixins.CreateModelMixin,GenericViewSet):
    serializer_class = XXXXXXX

 

*** 

  modelviewset --> 快速开发,复杂点的genericview、apiview

 类的继承关系,总共继承11个类

12、认证流程

- 如何编写?写类并实现authenticators
  请求进来认证需要编写一个类,类里面有一个authenticators方法,我们可以自定义这个方法,可以定制3类返回值。
  成功返回元组,返回none为匿名用户,抛出异常为认证失败。

源码流程:请求进来先走dispatch方法,然后封装的request对象会执行user方法,由user触发authenticators认证流程
- 方法中可以定义三种返回值:
    - (user,auth),认证成功
    - None , 匿名用户
    - 异常 ,认证失败
- 流程:
    - dispatch 
    - 再去request中进行认证处理

13、访问频率控制(redis应用场景)

- 匿名用户,根据用户IP或代理IP作为标识进行记录,为每一个用户在redis中创建一个列表
    {
        throttle_1.1.1.1:[1526868876.497521,152686885.497521...]
        throttle_1.1.1.2:[1526868876.497521,152686885.497521...]
        throttle_1.1.1.3:[1526868876.497521,152686885.497521...]
        throttle_1.1.1.4:[1526868876.497521,152686885.497521...]
        throttle_1.1.1.5:[1526868876.497521,152686885.497521...]
    }
    
    每个用户再来访问时,需要先去记录中剔除以及过期时间,再根据列表的长度判断是否可以继续访问。
    
    如何封IP:在防火墙中进行设置
- 注册用户,根据手机号或邮箱进行判断。
    {
        throttle_xxxx1:[1526868876.497521,152686885.497521...]
        throttle_xxxx2:[1526868876.497521,152686885.497521...]
        throttle_xxxx3:[1526868876.497521,152686885.497521...]
        throttle_xxxx4:[1526868876.497521,152686885.497521...]
    
    }
    
    每个用户再来访问时,需要先去记录中剔除以及过期时间,再根据列表的长度判断是否可以继续访问。

1分钟:40次

14:接口的幂等性?(是否会造成2次伤害)

一个接口通过1次相同的访问,再对该接口进行N次相同的访问时候,对资源不造影响,那么就认为接口具有幂等性。
比如:
GET, 第一次获取结果、第二次也是获取结果对资源都不会造成影响,幂等。
POST,第一次新增数据,第二次也会再次新增,非幂等。
PUT, 第一次更新数据,第二次不会再次更新,幂等。
PATCH,第一次更新数据,第二次不会再次更新,非幂等。
DELTE,第一次删除数据,第二次不在再删除,幂等。

restful规范

GET、POST、PUT、UPDATE/PATCH、DELETE

客户端通过以上4种http动词,对服务器资源进行操作,实现“表现层状态转化”;
【注意】
只是一种架构而不是标准,提供设计原则和约束条件。
适用于客户端与服务器交互类的软件,这是因为它更加简洁、有层次、更利于实现缓存等机制;

API设计

  • API与用户的通信协议,总是使用HTTPs协议
  • 域名 
    • https://api.example.com                         尽量将API部署在专用域名(会存在跨域问题)
    • https://example.org/api/                        API很简单
  • 版本
    • URL,如:https://api.example.com/v1/
    • 请求头                                                  跨域时,引发发送多次请求
  • 路径,视网络上任何东西都是资源,均使用名词表示(可复数)
    • https://api.example.com/v1/zoos
    • https://api.example.com/v1/animals
    • https://api.example.com/v1/employees
  • method
    • GET      :从服务器取出资源(一项或多项)
    • POST    :在服务器新建一个资源
    • PUT      :在服务器更新资源(客户端提供改变后的完整资源)
    • PATCH  :在服务器更新资源(客户端提供改变的属性)
    • DELETE :从服务器删除资源
  • 过滤,通过在url上传参的形式传递搜索条件
    • https://api.example.com/v1/zoos?limit=10:指定返回记录的数量
    • https://api.example.com/v1/zoos?offset=10:指定返回记录的开始位置
    • https://api.example.com/v1/zoos?page=2&per_page=100:指定第几页,以及每页的记录数
    • https://api.example.com/v1/zoos?sortby=name&order=asc:指定返回结果按照哪个属性排序,以及排序顺序
    • https://api.example.com/v1/zoos?animal_type_id=1:指定筛选条件
  • 错误处理,状态码是4xx时,应返回错误信息,error当做key。
    {
        error: "Invalid API key"
    }
  • 返回结果,针对不同操作,服务器向用户返回的结果应该符合以下规范。
  • 复制代码
    GET /collection:返回资源对象的列表(数组)
    GET /collection/resource:返回单个资源对象
    POST /collection:返回新生成的资源对象
    PUT /collection/resource:返回完整的资源对象
    PATCH /collection/resource:返回完整的资源对象
    DELETE /collection/resource:返回一个空文档
    复制代码
  • Hypermedia API,RESTful API最好做到Hypermedia,即返回结果中提供链接,连向其他API方法,使得用户不查文档,也知道下一步应该做什么。
    复制代码
    {"link": {
      "rel":   "collection https://www.example.com/zoos",
      "href":  "https://api.example.com/zoos",
      "title": "List of zoos",
      "type":  "application/vnd.yourformat+json"
    }}
    复制代码

12. Https和Http区别?

端口:
http:80
https: 443 
流程:
- 自定义证书
- 认证机构

源码后的心得

1:封装的思想:

  例如:将认证组件封装request里面,接着就只要requesst。user就能

2:开放,封闭思想(只要改配置即生效)

3:配置:中间件配置

question:你觉得python基础中哪些知识比较重要?
answer:我现在想到的,列表生成式、反射以及面向对象都比较重要,因为我最近在研究django rest-framework的源码,就发现这些知识在源码里面全部都用到了,比如说CBV是通过反射实现的,
比如说认证,原来我对封装的认识还不太够,后来看了这个源码,它里面有一种封装的思想就是把认证这个类封装到了request里面,以后要去用的时候,只需要调用它的方法,由它里边具体完成这个功能
问题

请求头

请求方法(8种)

请求方法:

get: 获得URI指定的资源,主要目的是获取
post:将主体传输到URI,主要目的是传输,而不是获取
put:传输文件,在请求主体中包含文件内容,然后保存到URI指定的位置。
head:只获取GET结果的头部,除去主体。
delete:删除文件,是PUT的逆操作
options:查询针对请求URI指定资源支持的方法。用法如:OPTIONS * HTTP/1.1
patch:HTTP/1.1协议中预留给能够将连接改为管道方式的代理服务器。
trace:回显服务器收到的请求,主要用于测试或诊断
注意:由于PUT和DELETE方法涉及服务器文件的直接操作,较为敏感,所以一般Web网站并不支持
除非配合使用了Web的验证机制。

 渲染器有坑

渲染器有坑 
- 指定渲染器只用JSON
- 视图:
  class UserView(...):
  queryset = []
  ...

assert 是的作用?断言
条件成立则继续往下,否则跑出异常,一般用于:满足某个条件之后,才能执行,否则应该跑出异常。

场景

class GenericAPIView(views.APIView):
                    """
                    Base class for all other generic views.
                    """
                    # You'll need to either set these attributes,
                    # or override `get_queryset()`/`get_serializer_class()`.
                    # If you are overriding a view method, it is important that you call
                    # `get_queryset()` instead of accessing the `queryset` property directly,
                    # as `queryset` will get evaluated only once, and those results are cached
                    # for all subsequent requests.
                    queryset = None
                    serializer_class = None

                    # If you want to use object lookups other than pk, set 'lookup_field'.
                    # For more complex lookup requirements override `get_object()`.
                    lookup_field = 'pk'
                    lookup_url_kwarg = None

                    # The filter backend classes to use for queryset filtering
                    filter_backends = api_settings.DEFAULT_FILTER_BACKENDS

                    # The style to use for queryset pagination.
                    pagination_class = api_settings.DEFAULT_PAGINATION_CLASS

                    def get_queryset(self):
                        """
                        Get the list of items for this view.
                        This must be an iterable, and may be a queryset.
                        Defaults to using `self.queryset`.

                        This method should always be used rather than accessing `self.queryset`
                        directly, as `self.queryset` gets evaluated only once, and those results
                        are cached for all subsequent requests.

                        You may want to override this if you need to provide different
                        querysets depending on the incoming request.

                        (Eg. return a list of items that is specific to the user)
                        """
                        assert self.queryset is not None, (
                            "'%s' should either include a `queryset` attribute, "
                            "or override the `get_queryset()` method."
                            % self.__class__.__name__
                        )

                        queryset = self.queryset
                        if isinstance(queryset, QuerySet):
                            # Ensure queryset is re-evaluated on each request.
                            queryset = queryset.all()
                        return queryset
源码

 

 

关联问题

TCP和UDP区别?

TCP协议是面向连接,保证高可靠性(数据无丢失,数据无失序,数据无错误,数据无重复达到)传输层协议
UDP:数据丢失,无秩序的传输层协议(qq基于udp协议)

三次握手、四次挥手

OSI 七层协议

scrapy框架

基本命令:
    scrapy startproject sp1 
    cd sp1
    scrapy genspider chouti chouti.com 
    
    scrapy crawl chouti --nolog 
    
编写:
    a. 起始URL
    
    b. parse函数
    
    c. pipelines 
    
    d. 解析器 

协程

协程本身不是并发
协程是"微线程"(在实际中是不存在的),是由程序员人为创造出来并控制程序:
  先执行某段代码、再跳到某处执行某段代码 如果遇到非IO请求来回切换:性能更低。 如果遇到IO(耗时)请求来回切换:性能高、实现并发(本质上利用IO等待的过程,再去干一些其他的事)

 前端

HTML、CSS、JS

框架和类库(模块)

- jQuery
- BootStrap
- Vue.js(与vue齐名的前端框架React和Angular)

响应式布局

响应式布局是通过@media实现的
@media (min-width:768px){
     .pg-header{
           background-color:green;
      }      
}
@media   (min-width:992px){
     .pg-header{
            background-color:pink;
     }
}    

jQuery选择器

基本选择器
id选择器:        $("#id值")
class选择器:    $(".c1")
所有元素选择器: $("*")
标签名选择器:   $("div")
组合选择器:      $("#i1, p")

层级选择器
后代选择器:         $("x 空格 y")---x的所有后代y(子子孙孙)
儿子选择器:         $("x  > y")---x的所有儿子y(儿子)
毗邻选择器:         $("x +y")---找到所有紧挨在x后面的y
兄弟选择器:         $("x ~y")---x之后所有的兄弟y

属性选择器
[attribute]                     //具有属性
[attribute=value]            // 属性等于
[attribute!=value]           // 属性不等

实例:
$("input[type='checkbox']");   // 取到checkbox类型的input标签
$("input[type!='text']");        // 取到类型不是text的input标签
注意当外层引用了双引号,内层应换为单引号


实例:
$("input[type='checkbox']");    // 取到checkbox类型的input标签
$("input[type!='text']");       // 取到类型不是text的input标签
View Code

Ajax

jQuery Ajax 和 原生ajax
jQuery ajax :$.ajax(..)
原生:ajax:XMLHttpRequest

跨域(jsonp、cors)

线上不会遇到跨域

JSONP

JSONP
jsonp是json用来跨域的一个东西。原理是通过script标签的跨域特性来绕过同源策略。
JSONP的简单实现模式,或者说是JSONP的原型:创建一个回调函数,然后在远程服务上调用这个函数并且将JSON 数据形式作为参数传递,
完成回调。

CORS

CORS
浏览器将CORS请求分成两类:简单请求(simple request)和复杂请求(not-so-simple request)。

简单请求
同时满足以下两大条件
(1) 请求方法是以下三种方法之一:
HEAD
GET
POST
(2)HTTP的头信息不超出以下几种字段:
Accept
Accept-Language
Content-Language
Last-Event-ID
Content-Type:只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain

凡是不同时满足上面两个条件,就属于非简单请求。

简单请求与复杂请求的区别

* 简单请求和非简单请求的区别?

   简单请求:一次请求
   非简单请求:两次请求,在发送数据之前会先发一次请求用于做“预检”,只有“预检”通过后才再发送一次请求用于数据传输。
* 关于“预检”

- 请求方式:OPTIONS
- “预检”其实做检查,检查如果通过则允许传输数据,检查不通过则不再发送真正想要发送的消息
- 如何“预检”
     => 如果复杂请求是PUT等请求,则服务端需要设置允许某请求,否则“预检”不通过
        Access-Control-Request-Method
     => 如果复杂请求设置了请求头,则服务端需要设置允许某请求头,否则“预检”不通过
        Access-Control-Request-Headers
支持跨域,简单请求

服务器设置响应头:Access-Control-Allow-Origin = '域名''*'

支持跨域,复杂请求

由于复杂请求时,首先会发送“预检”请求,如果“预检”成功,则发送真实数据。

“预检”请求时,允许请求方式则需服务器设置响应头:Access-Control-Request-Method
“预检”请求时,允许请求头则需服务器设置响应头:Access-Control-Request-Headers

web框架

你了解哪些Web框架和区别?(django、flask)

django和flask区别
对于django来说吧,内部组件就特别多。(比如from组件、modelfrom、admin、orm),功能也都挺完善的,(有那种大而成的感觉)

对于flask,内部组件就比较少了,但是有很多第三方组件组件来扩展它,(比如说wtform、Flask-SQLAlchemy,
这些组件其实也能堆建出来框架,)所以呢,他的可扩展行就比较强了,(所以对他本身来说有那种短小精悍的感觉) 这就是他们大体上的区别。 对于django它内部已经实现这些组件的功能,而flask不过只要通过第三方下载安装这些组件来实现这些功能 对于他们2个共同点:他们2个框架都没写socket,所以他们都是第三方模块wsgi,
django本身运行起来使用wsgiref 而flask是用Werkzeug WSGI 还有一个区别就是他们的请求管理不太一样:djagno是通过参数传递的,而flask是通过上下文管理机制 最重要的的是flask他有那个上下文管理机制,挺牛逼的 Django的Form主要具有以下功能? 生成HTMl标签,验证用户数据 is_vaild,HTML Form提交保留上次提交数据,初始化页面显示内容

django的form组件作用?

- 对用户请求的数据进行校验
- 生成HTML标签

PS:
- form对象是一个可迭代对象。
- 问题:choice的数据如果从数据库获取可能会造成数据无法实时更新
        - 重写构造方法,在构造方法中重新去数据库获取值。
        - ModelChoiceField字段
            from django.forms import Form
            from django.forms import fields
            from django.forms.models import ModelChoiceField
            class UserForm(Form):
                name = fields.CharField(label='用户名',max_length=32)
                email = fields.EmailField(label='邮箱')
                ut_id = ModelChoiceField(queryset=models.UserType.objects.all())    
            
            依赖:
                class UserType(models.Model):
                    title = models.CharField(max_length=32)

                    def __str__(self):
                        return self.title

django的Form组件存在的问题与modelform区别?应用场景?

 form对象是一个可迭代对象,字段需要自己手写。
  - 问题:choice的数据如果从数据库获取可能会造成数据无法实时更新
  - 重写构造方法,在构造方法中重新去数据库获取值。
  - ModelChoiceField字段

区别

           - 区别:
                - Form,字段需要自己手写。
                    class Form(Form):
                        xx = fields.CharField(.)
                        xx = fields.CharField(.)
                        xx = fields.CharField(.)
                        xx = fields.CharField(.)
                - ModelForm,可以通过Meta进行定义
                    class MForm(ModelForm):
                        class Meta:
                            fields = "__all__"
                            model = UserInfo
                            
 - 应用:只要是客户端向服务端发送表单数据时,都可以进行使用,如:用户登录注册
from django.forms import Form
                            from django.forms import fields
                            from django.forms.models import ModelChoiceField
                            class UserForm(Form):
                                name = fields.CharField(label='用户名',max_length=32)
                                email = fields.EmailField(label='邮箱')
                                ut_id = ModelChoiceField(queryset=models.UserType.objects.all())    
                            
                            依赖:
                                class UserType(models.Model):
                                    title = models.CharField(max_length=32)

                                    def __str__(self):
                                        return self.title
View Code

ContentType的作用?以及应用场景?

contenttype是django的一个组件(app),为我们找到django程序中所有app中的所有表并添加到记录中。

可以使用他再加上表中的两个字段实现:一张表和N张表创建FK关系。
- 字段:表名称
- 字段:数据行ID

应用:路飞表结构优惠券和专题课和学位课关联。

Django信号作用

django的信号其实就是django内部为开发者预留的一些自定制功能的钩子。
只要在某个信号中注册了函数,那么django内部执行的过程中就会自动触发注册在信号中的函数。
如: 

pre_init # django的modal执行其构造方法前,自动触发
post_init # django的modal执行其构造方法后,自动触发
pre_save # django的modal对象保存前,自动触发
post_save # django的modal对象保存后,自动触发

用信号做过什么?

在数据库某些表中添加数据时,可以进行日志记录。

csrf原理

目标:防止用户直接向服务端发起POST请求。
方案:先发送GET请求时,将token保存到:cookie、Form表单中(隐藏的input标签),以后再发送请求时只要携带过来即可。

 问题:如果想后台发送POST请求?
form表单提交:
                    <form method="POST">
                        {% csrf_token %}
                        <input type='text' name='user' />
                        <input type='submit' />
                    </form>
                ajax提交:
                    $.ajax({
                        url:'/index',
                        type:'POST',
                        data:{csrfmiddlewaretoken:'{{ csrf_token }}',name:'alex'}
                    })
                    前提:引入jquery + 引入jquery.cookie 
                    $.ajax({
                        url: 'xx',
                        type:'POST',
                        data:{name:'oldboyedu'},
                        headers:{
                            X-CSRFToken: $.cookie('csrftoken')
                        },
                        dataType:'json', // arg = JSON.parse('{"k1":123}')
                        success:function(arg){
                            
                        }
                    })

                    
                    优化方案:
                        <body>
                            <input type="button" onclick="Do1();"  value="Do it"/>
                            <input type="button" onclick="Do2();"  value="Do it"/>
                            <input type="button" onclick="Do3();"  value="Do it"/>

                            <script src="/static/jquery-3.3.1.min.js"></script>
                            <script src="/static/jquery.cookie.js"></script>
                            <script>
                                $.ajaxSetup({
                                    beforeSend: function(xhr, settings) {
                                        xhr.setRequestHeader("X-CSRFToken", $.cookie('csrftoken'));
                                    }
                                });

                                 function Do1(){
                                    $.ajax({
                                        url:"/index/",
                                        data:{id:1},
                                        type:'POST',
                                        success:function(data){
                                            console.log(data);
                                        }
                                    });
                                }

                                 function Do2(){
                                    $.ajax({
                                        url:"/index/",
                                        data:{id:1},
                                        type:'POST',
                                        success:function(data){
                                            console.log(data);
                                        }
                                    });
                                }

                                 function Do3(){
                                    $.ajax({
                                        url:"/index/",
                                        data:{id:1},
                                        type:'POST',
                                        success:function(data){
                                            console.log(data);
                                        }
                                    });
                                }
                            </script>
                        </body>

3种示例
三种

 

Django请求生命周期

用户请求进来   先走到   wsgi   然后请求就交给   jango的中间件  (穿过django中间件的方法是 process_request)    
接着是 路由匹配 路由匹配成功之后 就执行相应的 视图函数 在视图函数中可以调用orm做数据库操作 再从模板路径 将模板拿到 然后在后台经过渲染 渲染完成之后变成一个字符串 再把这个字符串经过所有中间件 和 wsgi 返回给用户

浏览器上输入地址,回车然后发生了什么?

关联到=> Http请求生命周期

- 域名解析--->浏览器会先到本地host文件(本地dns缓存)中找,找不到会取DNS服务器,DNS服务器请求解析该URL中的域名所对应的IP地址

- 连接成功--->,DNS服务器会将域名解析出IP地址后,根据IP地址和默认端口80,和服务器建立TCP连接

- 浏览器发送数据---->浏览器发出读取文件的http请求,该请求报文作为TCP,三次握手的第三个报文的数据发送给服务器

- 服务端接收数据并处理再响应---->服务器对浏览器请求做出响应,并把对应的html文件发送给浏览器

- 服务端做的事比较复杂---->释放TCP连接浏览器将该HMTL渲染并显示内容

,如果是:Django、Flask

浏览器会先到本地host文件(本地dns缓存)中找,找不到会取DNS服务器,DNS服务器请求解析该URL中的域名所对应的IP地址,
根据ip找到对应的web服务器,这样你和服务器连接就建立了,然后就等着收发消息

什么是wsgi

是web服务网关接口,是一套协议。以下模块实现了wsgi协议:
    - wsgiref
    - werkzurg
    - uwsgi   关于部署
以上模块本质:编写socket服务端,用于监听请求,当有请求到来,则将请求数据进行封装,然后交给web框架处理
    from wsgiref.simple_server import make_server

    def run_server(environ, start_response):
        """
        environ: 封装了请求相关的数据
        start_response:用于设置响应头相关数据
        """
        start_response('200 OK', [('Content-Type', 'text/html')])
        return [bytes('<h1>Hello, web!</h1>', encoding='utf-8'), ]
     
     
    if __name__ == '__main__':
        httpd = make_server('', 8000, run_server)
        httpd.serve_forever()

Django源码:wsgi

class WSGIHandler(base.BaseHandler):
    request_class = WSGIRequest

    def __init__(self, *args, **kwargs):
        super(WSGIHandler, self).__init__(*args, **kwargs)
        self.load_middleware()

    def __call__(self, environ, start_response):
        # 请求刚进来之后 #
        
        set_script_prefix(get_script_name(environ))
        signals.request_started.send(sender=self.__class__, environ=environ)
        request = self.request_class(environ)
        response = self.get_response(request)

        response._handler_class = self.__class__

        status = '%d %s' % (response.status_code, response.reason_phrase)
        response_headers = [(str(k), str(v)) for k, v in response.items()]
        for c in response.cookies.values():
            response_headers.append((str('Set-Cookie'), str(c.output(header=''))))
        start_response(force_str(status), response_headers)
        if getattr(response, 'file_to_stream', None) is not None and environ.get('wsgi.file_wrapper'):
            response = environ['wsgi.file_wrapper'](response.file_to_stream)
        return response

中间件

作用

对所有的请求进行批量处理,在视图函数执行前后进行自定义操作。

应用用户登录校验/, 为什么:如果不使用就需要为每个函数添加装饰器,太繁琐

权限处理/      why? 用户登录后,将权限放到session中,然后再每次请求进来在中间件里,根据当前的url去session中匹配,
判断当前用户是否有权限访问当前url,有权限就继续访问,没有就返回,
(检查的东西就可以放到中间件中进行统一处理)在process_request方法里面做的,
        我们的中间件是放在session后面,因为中间件需要到session里面取数据
          
还有一些内置:
  CSRF:对所有的post请求做验证/ 将jango生成的一串字符串发送给我们,1种是从请求体发过来,一种是放在隐藏的标签里面
       用的是process_view 
  session/
  缓存/ 常用的数据放在缓存里面,就不用走视图函数,请求进来通过所有的process_request,会到缓存里面查数据,有就直接拿,
        没有就走视图函数
      关键点:1:执行完所有的process_request才去缓存取数据
          2:执行完所有的process_response才将数据放到缓存
  日志记录
另外,还有一个就是处理:cors跨域(场景:前后端分离时,本地测试开发时使用)
        如果网站之间存在跨域,域名不同,端口不同会导致出现跨域,但凡出现跨域,浏览器就会出现同源策略的限制
        解决:在我们的服务端给我们响应数据,加上响应头---> 在中间件加的

关于缓存问题

1:为什么放在最后一个process_request才去缓存

因为需要验证完用户的请求,才能返回数据

2:什么时候将数据放到缓存中

第一次走中间件,缓存没有数据,会走视图函数,取数据库里面取数据,

当走完process_response,才将数据放到缓存里,因为,走process_response的时候可能给我们的响应加处理

为什么要用缓存?

将常用且不太频繁修改的数据放入缓存。
以后用户再来访问,先去缓存查看是否存在,如果有就返回
否则,去数据库中获取并返回给用户(再加入到缓存,以便下次访问)

django内部支持哪些缓存?

Django中提供了6种缓存方式:
  开发调试(不加缓存)
  内存
  文件
  数据库
  Memcache缓存(python-memcached模块)
  Memcache缓存(pylibmc模块)

安装第三方组件支持redis:
  django-redis组件

设置缓存

- 全站缓存
- 视图函数缓存
- 局部模板缓存

中间件中方法(五个)

process_request(self,request)  先走request 通过路由匹配返回
process_view(self, request, callback, callback_args, callback_kwargs) 再返回执行view
process_template_response(self,request,response)   当视图函数的返回值
process_exception(self, request, exception)  当视图函数的返回值对象中有render方法时,该方法才会被调用
process_response(self, request, response)

执行流程

第一次缓存里面没有数据,

注意:

  对于所有请求的批量做处理的时候用中间件
  单独对某几个函数做处理的时候用装饰器

csrf本质

目标:防止用户直接向服务端发起POST请求
如何防止:
- 用户先发送GET获取csrf token: Form表单中一个隐藏的标签 + cookie - 发起POST请求时,需要携带之前发送给用户的csrf token; - 在中间件的process_view方法中进行校验。

问题:如何想后台发送POST请求

爬虫:
     reqeusts.post()
ajax
$.ajax({
    url:'/index',
    type:'POST',
    data:{csrfmiddlewaretoken:'{{ csrf_token }}',name:'alex'}
})
from表单
<form method="POST">
    {% csrf_token %}
    <input type='text' name='user' />
    <input type='submit' />
</form>

 关联ajax问题

路由

url+关系

反向解析---> 写个名称 reverse

路由分发,本质返回一个元组,里面三个元素,第一是列表

视图

基于函数  fbv

 # FBV 写法
# urls.py
 url(r'^login/$',views.login, name="login"),

# views.py
def login(request):
    if request.method == "POST":
        print(request.POST)

    return render(request,"login.html")

# HTML
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>登录页面</title>
</head>
<body>
<form action="{% url 'login' %}" method="post" enctype="multipart/form-data">
    <input type="text" name="user2">
    <input type="file" name="file">
    <input type="submit" value="提交">


</form>
</body>
</html>

CBV 基于类

# urls.py    
url(r'^login/$',views.Login.as_view(), name="login"), 

# views.py
from django.views import View
class Login(View):   # 类首字母大写
    def get(self,request):
        return render(request,"login.html")
    def post(self,request):
        print(request.POST)
        return HttpResponse("OK")

加装饰器

=================================
class IndexView(View):
    
    # 如果是crsf相关,必须放在此处
    def dispach(self,request):
        # 通过反射执行post/get
    
    @method_decoretor(装饰器函数)
    def get(self,request):
        pass
        
    def post(self,request):
        pass
路由:IndexView.as_view()

FBV和CBV的区别

- 没什么区别,因为他们的本质都是函数。CBV的.as_view()返回的view函数,view函数中调用类的dispatch方法,
在dispatch方法中通过反射执行get/post/delete/put等方法。 非要说区别的话: - CBV比较简洁,GET/POST等业务功能分别放在不同get/post函数中。FBV自己做判断进行区分。

CBV的注意事项

添加装饰器
方式一:
from django.views import View
from django.utils.decorators import method_decorator  ---> 需要引入memethod_decorator

def auth(func):
    def inner(*args,**kwargs):
        return func(*args,**kwargs)
    return inner

class UserView(View):
    @method_decorator(auth)
    def get(self,request,*args,**kwargs):
        return HttpResponse('...')    

方式二:
- csrf的装饰器要加到dispath前面
from django.views import View
from django.utils.decorators import method_decorator
from django.views.decorators.csrf import csrf_exempt,csrf_protect   ---> 需要引入 csrf_exempt


class UserView(View):
    @method_decorator(csrf_exempt)
    def dispatch(self, request, *args, **kwargs):
        return HttpResponse('...')

或者:
from django.views import View
from django.utils.decorators import method_decorator
from django.views.decorators.csrf import csrf_exempt,csrf_protect

@method_decorator(csrf_exempt,name='dispatch')  --->  指定名字
class UserView(View):
    def dispatch(self, request, *args, **kwargs):
        return HttpResponse('...')

注意

能从request里能取到什么:(request里面有什么)--->请求发过来什么,全部都能拿到
        GET、POST、method、cookie、session、files、body(这7个为基础)

 ORM

 增删改查
- values/values_list
- F/Q
- 性能相关
- 原生SQL

 增

增:两种(以字典的形式增的话,需打散)
  # 增加一条数据,可以接受字典类型数据 **kwargs
      1. models.User.objects.create(**{}) 直接创建并且提交到数据库
      2. user = models.User(**{})  user.save()

models.UserInfo.objects.create()

obj = models.UserInfo(name='xx')
obj.save()

找到对象删除
   models.User.objects.filter().delete()

更新 两种
  1. models.User.objects.filter().update(username="一枝花")  支持**kwargs
  2. user = models.User.objects.get(id=1) 
    user.username="梁志华"
   user.save()

models.User.objects.all()   # 获取全部
models.User.objects.get(条件)  不满足条件就报错|返回的是一个对象
models.User.objects.filter(条件) 不满足条件不报错,但是返回的结果是一个对象的列表
models.User.objects.filter().first()
models.User.objects.filter().last()
            
models.User.objects.exclude(条件)  # 不包含指定条件

常用操作               

order_by

 # order by

# models.Tb1.objects.filter(name='seven').order_by('id')    # asc
# models.Tb1.objects.filter(name='seven').order_by('-id')   # desc

group_by

group by # from django.db.models import Count, Min, Max, Sum
 # models.Tb1.objects.filter(c1=1).values('id').annotate(c=Count('num'))
 # SELECT "app01_tb1"."id", COUNT("app01_tb1"."num") AS "c" FROM "app01_tb1" WHERE "app01_tb1"."c1" = 1 GROUP BY "app01_tb1"."id"

靠近原生SQL-->extra\raw

- extra
    def extra(self, select=None, where=None, params=None, tables=None, order_by=None, 
select_params=None) # 构造额外的查询条件或者映射,如:子查询 Entry.objects.extra(select={'new_id': "select col from sometable where othercol > %s"},
select_params=(1,)) Entry.objects.extra(where=['headline=%s'], params=['Lennon']) Entry.objects.extra(where=["foo='a' OR bar = 'a'", "baz = 'a'"]) Entry.objects.extra(select={'new_id': "select id from tb where id > %s"}, s
elect_params=(1,), order_by=['-nid'])
- raw 
def raw(self, raw_query, params=None, translations=None, using=None):
    # 执行原生SQL
    models.UserInfo.objects.raw('select * from userinfo')

    # 如果SQL是其他表时,必须将名字设置为当前UserInfo对象的主键列名
    models.UserInfo.objects.raw('select id as nid,name as title  from 其他表')

    # 为原生SQL设置参数
    models.UserInfo.objects.raw('select id as nid from userinfo where nid>%s', params=[12,])

    # 将获取的到列名转换为指定列名
    name_map = {'first': 'first_name', 'last': 'last_name', 'bd': 'birth_date', 'pk': 'id'}
    Person.objects.raw('SELECT * FROM some_other_table', translations=name_map)

    # 指定数据库
    models.UserInfo.objects.raw('select * from userinfo', using="default")

原生sql-->connection

from django.db import connection, connections
cursor = connection.cursor()  # cursor = connections['default'].cursor()
cursor.execute("""SELECT * from auth_user where id = %s""", [1])
row = cursor.fetchone() # fetchall()/fetchmany(..)

高级一点

F

# F
#
# from django.db.models import F
# models.Tb1.objects.update(num=F('num')+1)

Q

# 方式一:
        # Q(nid__gt=10)
        # Q(nid=8) | Q(nid__gt=10)
        # Q(Q(nid=8) | Q(nid__gt=10)) & Q(caption='root')

        # 方式二:
        # con = Q()
        # q1 = Q()
        # q1.connector = 'OR'
        # q1.children.append(('id', 1))
        # q1.children.append(('id', 10))
        # q1.children.append(('id', 9))
        # q2 = Q()
        # q2.connector = 'OR'
        # q2.children.append(('c1', 1))
        # q2.children.append(('c1', 10))
        # q2.children.append(('c1', 9))
        # con.add(q1, 'AND')
        # con.add(q2, 'AND')
        #
        # models.Tb1.objects.filter(con)

select_related/ prefech_related

def select_related(self, *fields)
     性能相关:表之间进行join连表操作,一次性获取关联的数据。
     model.tb.objects.all().select_related()
     model.tb.objects.all().select_related('外键字段')
     model.tb.objects.all().select_related('外键字段__外键字段')

def prefetch_related(self, *lookups)
    性能相关:多表连表操作时速度会慢,使用其执行多次SQL查询在Python代码中实现连表操作。
            # 获取所有用户表
            # 获取用户类型表where id in (用户表中的查到的所有用户ID)
            models.UserInfo.objects.prefetch_related('外键字段')



            from django.db.models import Count, Case, When, IntegerField
            Article.objects.annotate(
                numviews=Count(Case(
                    When(readership__what_time__lt=treshold, then=1),
                    output_field=CharField(),
                ))
            )

            students = Student.objects.all().annotate(num_excused_absences=models.Sum(
                models.Case(
                    models.When(absence__type='Excused', then=1),
                default=0,
                output_field=models.IntegerField()
            )))
补充
# 1次SQL
# select * from userinfo
objs = UserInfo.obejcts.all()
for item in objs:
    print(item.name)
    
# n+1次SQL
# select * from userinfo
objs = UserInfo.obejcts.all()
for item in objs:
    # select * from usertype where id = item.id 
    print(item.name,item.ut.title)
    

# 1次SQL
# select * from userinfo inner join usertype on userinfo.ut_id = usertype.id 
objs = UserInfo.obejcts.all().select_related('ut')  连表查询
for item in objs:
    print(item.name,item.ut.title)
            
.prefetch_related()
    # select * from userinfo where id <= 8
    # 计算:[1,2]
    # select * from usertype where id in [1,2]
    objs = UserInfo.obejcts.filter(id__lte=8).prefetch_related('ut')
    for obj in objs:
        print(obj.name,obj.ut.title)

其他

def annotate(self, *args, **kwargs)
    # 用于实现聚合group by查询

    from django.db.models import Count, Avg, Max, Min, Sum

    v = models.UserInfo.objects.values('u_id').annotate(uid=Count('u_id'))
    # SELECT u_id, COUNT(ui) AS `uid` FROM UserInfo GROUP BY u_id

    v = models.UserInfo.objects.values('u_id').annotate(uid=Count('u_id')).filter(uid__gt=1)
    # SELECT u_id, COUNT(ui_id) AS `uid` FROM UserInfo GROUP BY u_id having count(u_id) > 1

    v = models.UserInfo.objects.values('u_id').annotate(uid=Count('u_id',distinct=True)).filter(uid__gt=1)
    # SELECT u_id, COUNT( DISTINCT ui_id) AS `uid` FROM UserInfo GROUP BY u_id having count(u_id) > 1

def distinct(self, *field_names)
    # 用于distinct去重
    models.UserInfo.objects.values('nid').distinct()
    # select distinct nid from userinfo

    注:只有在PostgreSQL中才能使用distinct进行去重

def order_by(self, *field_names)
    # 用于排序
    models.UserInfo.objects.all().order_by('-id','age')

def extra(self, select=None, where=None, params=None, tables=None, order_by=None, select_params=None)
    # 构造额外的查询条件或者映射,如:子查询

    Entry.objects.extra(select={'new_id': "select col from sometable where othercol > %s"}, select_params=(1,))
    Entry.objects.extra(where=['headline=%s'], params=['Lennon'])
    Entry.objects.extra(where=["foo='a' OR bar = 'a'", "baz = 'a'"])
    Entry.objects.extra(select={'new_id': "select id from tb where id > %s"}, select_params=(1,), order_by=['-nid'])

 def reverse(self):
    # 倒序
    models.UserInfo.objects.all().order_by('-nid').reverse()
    # 注:如果存在order_by,reverse则是倒序,如果多个排序则一一倒序


 def defer(self, *fields):
    models.UserInfo.objects.defer('username','id')
    或
    models.UserInfo.objects.filter(...).defer('username','id')
    #映射中排除某列数据

 def only(self, *fields):
    #仅取某个表中的数据
     models.UserInfo.objects.only('username','id')
     或
     models.UserInfo.objects.filter(...).only('username','id')

 def using(self, alias):
     指定使用的数据库,参数为别名(setting中的设置)


##################################################
# PUBLIC METHODS THAT RETURN A QUERYSET SUBCLASS #
##################################################

def raw(self, raw_query, params=None, translations=None, using=None):
    # 执行原生SQL
    models.UserInfo.objects.raw('select * from userinfo')

    # 如果SQL是其他表时,必须将名字设置为当前UserInfo对象的主键列名
    models.UserInfo.objects.raw('select id as nid from 其他表')

    # 为原生SQL设置参数
    models.UserInfo.objects.raw('select id as nid from userinfo where nid>%s', params=[12,])

    # 将获取的到列名转换为指定列名
    name_map = {'first': 'first_name', 'last': 'last_name', 'bd': 'birth_date', 'pk': 'id'}
    Person.objects.raw('SELECT * FROM some_other_table', translations=name_map)

    # 指定数据库
    models.UserInfo.objects.raw('select * from userinfo', using="default")

    ################### 原生SQL ###################
    from django.db import connection, connections
    cursor = connection.cursor()  # cursor = connections['default'].cursor()
    cursor.execute("""SELECT * from auth_user where id = %s""", [1])
    row = cursor.fetchone() # fetchall()/fetchmany(..)


def values(self, *fields):
    # 获取每行数据为字典格式

def values_list(self, *fields, **kwargs):
    # 获取每行数据为元祖

def dates(self, field_name, kind, order='ASC'):
    # 根据时间进行某一部分进行去重查找并截取指定内容
    # kind只能是:"year"(年), "month"(年-月), "day"(年-月-日)
    # order只能是:"ASC"  "DESC"
    # 并获取转换后的时间
        - year : 年-01-01
        - month: 年-月-01
        - day  : 年-月-日

    models.DatePlus.objects.dates('ctime','day','DESC')

def datetimes(self, field_name, kind, order='ASC', tzinfo=None):
    # 根据时间进行某一部分进行去重查找并截取指定内容,将时间转换为指定时区时间
    # kind只能是 "year", "month", "day", "hour", "minute", "second"
    # order只能是:"ASC"  "DESC"
    # tzinfo时区对象
    models.DDD.objects.datetimes('ctime','hour',tzinfo=pytz.UTC)
    models.DDD.objects.datetimes('ctime','hour',tzinfo=pytz.timezone('Asia/Shanghai'))

    """
    pip3 install pytz
    import pytz
    pytz.all_timezones
    pytz.timezone(‘Asia/Shanghai’)
    """

def none(self):
    # 空QuerySet对象


####################################
# METHODS THAT DO DATABASE QUERIES #
####################################

def aggregate(self, *args, **kwargs):
   # 聚合函数,获取字典类型聚合结果
   from django.db.models import Count, Avg, Max, Min, Sum
   result = models.UserInfo.objects.aggregate(k=Count('u_id', distinct=True), n=Count('nid'))
   ===> {'k': 3, 'n': 4}

def count(self):
   # 获取个数

def get(self, *args, **kwargs):
   # 获取单个对象

def create(self, **kwargs):
   # 创建对象

def bulk_create(self, objs, batch_size=None):
    # 批量插入
    # batch_size表示一次插入的个数
    objs = [
        models.DDD(name='r11'),
        models.DDD(name='r22')
    ]
    models.DDD.objects.bulk_create(objs, 10)

def get_or_create(self, defaults=None, **kwargs):
    # 如果存在,则获取,否则,创建
    # defaults 指定创建时,其他字段的值
    obj, created = models.UserInfo.objects.get_or_create(username='root1', defaults={'email': '1111111','u_id': 2, 't_id': 2})

def update_or_create(self, defaults=None, **kwargs):
    # 如果存在,则更新,否则,创建
    # defaults 指定创建时或更新时的其他字段
    obj, created = models.UserInfo.objects.update_or_create(username='root1', defaults={'email': '1111111','u_id': 2, 't_id': 1})

def first(self):
   # 获取第一个

def last(self):
   # 获取最后一个

def in_bulk(self, id_list=None):
   # 根据主键ID进行查找
   id_list = [11,21,31]
   models.DDD.objects.in_bulk(id_list)

def delete(self):
   # 删除

def update(self, **kwargs):
    # 更新

def exists(self):
   # 是否有结果

F的使用:数字自增和字符串更新

# 数字自增 只能用数字
from django.db.models import F
models.UserInfo.objects.update(num=F('num') + 1)

# 字符串更新
from django.db.models.functions import Concat
from django.db.models import Value

models.UserInfo.objects.update(name=Concat('name', 'pwd'))
models.UserInfo.objects.update(name=Concat('name', Value('666')))

 MTV和MVC

MVC: model view controller
MTV: model tempalte view

遇到的难题:

  - CBV是添加csrf装饰器
  - 多数据库配置 allow_relation方法

多数据库相关操作

相关执行命令
python manage.py makemigraions
python manage.py migrate app名称 --databse=配置文件数据名称的别名


手动操作:
models.UserType.objects.using('db1').create(title='普通用户')
result = models.UserType.objects.all().using('default')

自动操作:
class Router1:
def db_for_read(self, model, **hints):
    """
    Attempts to read auth models go to auth_db.
    """
    return 'db1'

def db_for_write(self, model, **hints):
    """
    Attempts to write auth models go to auth_db.
    """
    return 'default'

配置:
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
    },
    'db1': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': os.path.join(BASE_DIR, 'db1.sqlite3'),
    },
}
DATABASE_ROUTERS = ['db_router.Router1',]

使用:
models.UserType.objects.create(title='VVIP')

result = models.UserType.objects.all()
print(result)

补充-->精细粒度

粒度更细
class Router1:
    def db_for_read(self, model, **hints):
        """
        Attempts to read auth models go to auth_db.
        """
        if model._meta.model_name == 'usertype':
            return 'db1'
        else:
            return 'default'

    def db_for_write(self, model, **hints):
        """
        Attempts to write auth models go to auth_db.
        """
        return 'default'

问题:app01中的表在default数据库创建app02中的表在db1数据库创建

问题: 
app01中的表在default数据库创建
app02中的表在db1数据库创建

# 第一步:
python manage.py makemigraions 

# 第二步:
app01中的表在default数据库创建
python manage.py migrate app01 --database=default

# 第三步:
app02中的表在db1数据库创建
python manage.py migrate app02 --database=db1

# 手动操作:
m1.UserType.objects.using('default').create(title='VVIP')
m2.Users.objects.using('db1').create(name='VVIP',email='xxx')
# 自动操作:
配置: 
class Router1:
    def db_for_read(self, model, **hints):
        """
        Attempts to read auth models go to auth_db.
        """
        if model._meta.app_label == 'app01':
            return 'default'
        else:
            return 'db1'

    def db_for_write(self, model, **hints):
        """
        Attempts to write auth models go to auth_db.
        """
        if model._meta.app_label == 'app01':
            return 'default'
        else:
            return 'db1'

DATABASE_ROUTERS = ['db_router.Router1',]

使用: 
m1.UserType.objects.using('default').create(title='VVIP')
m2.Users.objects.using('db1').create(name='VVIP',email='xxx')
其他:
数据库迁移时进行约束:
class Router1:
    def allow_migrate(self, db, app_label, model_name=None, **hints):
        """
        All non-auth models end up in this pool.
        """
        if db=='db1' and app_label == 'app02':
            return True
        elif db == 'default' and app_label == 'app01':
            return True
        else:
            return False
        
        # 如果返回None,那么表示交给后续的router,如果后续没有router,则相当于返回True
        
    def db_for_read(self, model, **hints):
        """
        Attempts to read auth models go to auth_db.
        """
        if model._meta.app_label == 'app01':
            return 'default'
        else:
            return 'db1'

    def db_for_write(self, model, **hints):
        """
        Attempts to write auth models go to auth_db.
        """
        if model._meta.app_label == 'app01':
            return 'default'
        else:
            return 'db1'

 websocket

什么是轮询

通过定时器让程序每隔n秒执行一次操作。

什么是长轮询

客户端向服务器发送请求,服务器接到请求后hang住连接,等待30秒,30s过后再重新发起请求,
直到有新消息才返回响应信息并关闭连接,客户端处理完响应信息后再向服务器发送新的请求。

轮询和长轮询的目的

由于Http请求是无状态、短连接所以服务端无法向客户端实时推送消息,
所以,我们就是可以使用:轮训和长轮训去服务端获取实时数据。

什么是websocket

websocket是给浏览器新建的一套(类似与http)协议,协议规定:(\r\n分割)浏览器和服务器连接之后不断开,
    以此完成:服务端向客户端主动推送消息。 websocket协议额外做的一些操作 握手
----> 连接钱进行校验 加密 ----> payload_len=127/126/<=125 --> mask key

http协议

  HTTP协议本质:通过\r\n分割的规范+ 请求响应之后断开链接   ==  >  无状态、 短连接
具体:
  Http协议是建立在tcp之上的,是一种规范,它规范定了发送的数据的数据格式,
然而这个数据格式是通过\r\n 进行分割的,请求头与请求体也是通过2个\r\n分割的,响应的时候,
响应头与响应体也是通过\r\n分割,并且还规定已请求已响应就会断开链接   
即--->  短连接、无状态

websocket本质

创建一个连接后不断开的socket
当连接成功之后:
    客户端(浏览器)会自动向服务端发送消息,包含: Sec-WebSocket-Key: iyRe1KMHi4S4QXzcoboMmw==
    服务端接收之后,会对于该数据进行加密:base64(sha1(swk + magic_string))
    构造响应头:
            HTTP/1.1 101 Switching Protocols\r\n
            Upgrade:websocket\r\n
            Connection: Upgrade\r\n
            Sec-WebSocket-Accept: 加密后的值\r\n
            WebSocket-Location: ws://127.0.0.1:8002\r\n\r\n        
    发给客户端(浏览器)
建立:双工通道,接下来就可以进行收发数据
    发送数据是加密,解密,根据payload_len的值进行处理
        payload_len <= 125
        payload_len == 126
        payload_len == 127
    获取内容:
        mask_key
        数据
        根据mask_key和数据进行位运算,就可以把值解析出来。

 Flask

问题一:flask和django的区别:

django:大而全的全的框架,重武器;内置很多组件:ORM、admin、Form、ModelForm、中间件、信号、缓存、csrf等
flask: 微型框架、可扩展强,如果开发简单程序使用flask比较快速,如果实现负责功能就需要引入一些组件:
      flask-session/flask-SQLAlchemy/wtforms/flask-migrate/flask-script/blinker

这两个框架都是基于wsgi协议实现的,默认使用的wsgi模块不一样。
还有一个显著的特点,他们处理请求的方式不同:
django: 通过将请求封装成Request对象,再通过参数进行传递。
flask:通过上下文管理实现。
问题1.1: flask上下文管理:
  简单来说,falsk上下文管理可以分为三个阶段:
        1、请求进来时,将请求相关的数据放入上下问管理中
        2、在视图函数中,要去上下文管理中取值
        3、请求响应,要将上下文管理中的数据清除
  
  详细点来说:
        1、请求刚进来,将request,session封装在RequestContext类中,app,g封装在AppContext类中,并通过LocalStack将requestcontext和appcontext放入Local类中
        2、视图函数中,通过localproxy--->偏函数--->localstack--->local取值
        3、请求相应时,先执行save.session()再各自执行pop(),将local中的数据清除
        

问题1.2  flask第三方组件
  flask:
      -flask-session    默认放入cookie,可以放入redis
      -flask-redis
      -flask-migrate
      -flask-script
      -blinker  信号
   公共: DBUtils      数据库连接池
      wtforms       表单验证+生成HTML标签
      sqlalchemy
  自定义:Auth   参考falsk-login

问题二:Flask中的session是什么时候创建,什么时候销毁的?
  当请求进来时,会将requset和session封装为一个RequestContext对象,通过LocalStack将RequestContext放入到Local对象中,因为
请求第一次来session是空值,所以执行open_session,给session(uuid4())赋值,再通过视图函数处理,请求响应时执行save.session,将签名session写入cookie中,
  再将Local中的数值pop掉。

问题三:flask中一共有几个LocalStack和Local对象
  两个LocalStack,两个Local
  request、session共同用一个LocalStack和Local
  g、app共同用一个Localstack和Local

问题四: 为什么把请求放到RequestContext中:
   因为request和session都是在视图中操作频繁的数据,也是用户请求需要用的数据,将request和session封装在RequestContext中top,
  pop一次就可以完成,而单独不封装在一起就会多次操作,

    ctx = RequestContext(request,session)

问题五:local作用
    -保存    请求上下文对象和app上下文对象

     -localstack的源码与threading.local(线程处理)作用相似,不同之处是Local是通过greenlet(协程)获取唯一标识,粒度更细
      
问题六:Localstack作用
    2、将local对象中的数据维护成一个栈【ctx,ctx】(先进后出)
         {
            “协程或线程的唯一标识”: { stack:[ctx,ctx,ctx,] }
         }
    

为什么维护成一个栈?
    当是web应用时:不管是单线程还是多线程,栈中只有一个数据
   - 服务端单线程:
        {
            111:{stack: [ctx, ]}
        }
   - 服务端多线程:
        {
            111:{stack: [ctx, ]}
            112:{stack: [ctx, ]}
        }
离线脚本:可以在栈中放入多个数据,在任何情况下都可以获取到当前app的请求和响应
with app01.app_context():
                      print(current_app)
                      with app02.app_context():
                            print(current_app)
                      print(current_app)

 
问题七:什么是g?
    g 相当于一次请求的全局变量,当请求进来时将g和current_app封装为一个APPContext类,在通过LocalStack将Appcontext放入Local中,
  取值时通过偏函数,LocalStack、loca l中取值,响应时将local中的g数据删除:
 
问题八:怎么获取Session/g/current_app/request
    通过 、偏函数(lookup_req_object)、Localstack、Local取值
1、反射
  -CBV
  -django配置文件
  -wtforms中的Form()示例化中 将"_fields中的数据封装到From类中"

class Stack():
    def __init__(self,size):
        self.size=size
        self.stack=[]

    def getstack(self):
        """
        #获取栈当前数据
        :return:
        """
        return self.stack

    def __str__(self):
        return str(self.stack)

    def top(self,x):
    # 入栈之前检查栈是否已满
        if self.isfull():
            raise Exception("stack is full")
        else:
            self.stack.append(x)

    def pop(self):
        """
        # 出栈之前检查栈是否已空
        :return:
        """
        if self.isempty():
            raise Exception("stack is empty")
        else:
            self.stack.pop()

    def isfull(self):
        """
        判断栈满
        :return:
        """
        if len(self.stack)==self.size:
            return True
        return False

    def isempty(self):
        """
        判断栈空
        :return:
        """
        if len(self.stack)==0:
            return True
        return False

if __name__ == '__main__' :
    stack=Stack(4)

    for i in range(11):
        stack.top(i)
        print(stack.getstack())

    for i in range(3):
        stack.pop()
        print(stack.getstack())

 redis

基本操作

- 连接
- 直接连接:
    import redis 
    r = redis.Redis(host='10.211.55.4', port=6379)
    r.set('foo', 'Bar')
    print r.get('foo')
- 连接池:
    import redis
    pool = redis.ConnectionPool(host='10.211.55.4', port=6379)
     
    r = redis.Redis(connection_pool=pool)
    r.set('foo', 'Bar')
    print r.get('foo')

5大数据类型

- 5大数据类型
- 字符串 "sadf"  (只有字符串有默认的超时时间)
    - set('k1','123',ex=10)
    - get 
    - mset
    - mget
    - incr
    - 超时时间:
        import redis 
        r = redis.Redis(host='10.211.55.4', port=6379)
        r.set('foo', 'Bar',ex=10)

- 字典 {'k1':'v1'} - hset(name, key, value) - hmset(name, mapping) - hget(name,key) - 超时时间(字典,列表、集合、有序结合相同):自定义 import redis conn = redis.Redis(host='10.211.55.4', port=6379) conn.hset('n1', 'k1','123123') conn.expire('n1',10) - 如果一个字典在redis中保存了10w个值,我需要将所有值全部循环并显示,请问如何实现? for item in r.hscan_iter('k2',count=100): print item - 列表 [11,11,22,33] 没有默认的超时时间 - lpush - rpush - lpop - blpop - rpop - brpop - llen - lrange - 如果一个列表在redis中保存了10w个值,我需要将所有值全部循环并显示,请问如何实现? 一个一个取值,列表没有iter方法,但能自定义
     def list_scan_iter(name,count
=3): start = 0 while True: result = conn.lrange(name, start, start+count-1) start += count if not result: break for item in result: yield item for val in list_scan_iter('num_list'): print(val)
  场景:投票系统,script-redis
- 集合 {'alex','oldboy','日天'} - 有序集合 {('alex',59),('oldboy',100),('日天',1)}
- 公共操作:
    - delete(*names)
    - keys(pattern='*')
    expire(name ,time)
    ...

内置有16个小数据库(分离)

事务

import redis

pool = redis.ConnectionPool(host='10.211.55.4', port=6379)

conn = redis.Redis(connection_pool=pool)

# pipe = r.pipeline(transaction=False)
pipe = conn.pipeline(transaction=True)
# 开始事务
pipe.multi()

pipe.set('name', 'alex')
pipe.set('role', 'sb')
pipe.lpush('roless', 'sb')

# 提交
pipe.execute()

注意:咨询是否当前分布式redis是否支持事务

检测,watch

假如开发商城,剩余多少件商品,如何控制剩余数量不会出问题?

方案一:通过redis的watch实现
import redis conn
= redis.Redis(host='127.0.0.1',port=6379) # conn.set('count',1000) val = conn.get('count') print(val) with conn.pipeline(transaction=True) as pipe: # 先监视,自己的值没有被修改过 conn.watch('count') # 事务开始 pipe.multi() old_count = conn.get('count') count = int(old_count) print('现在剩余的商品有:%s',count) input("问媳妇让不让买?") pipe.set('count', count - 1) # 执行,把所有命令一次性推送过去 pipe.execute()

 方案二:通过数据库,加锁for update

发布和订阅      

只要有任务就所有订阅者每人一份

发布者:
    import redis

    conn = redis.Redis(host='127.0.0.1',port=6379)
    conn.publish('104.9MH', "hahahahahaha")
订阅者:
    import redis

    conn = redis.Redis(host='127.0.0.1',port=6379)
    pub = conn.pubsub()
    pub.subscribe('104.9MH')

    while True:
        msg= pub.parse_response()
        print(msg)

主从复制

优势:
    - 高可用
    - 分担主压力
注意: 
    - slave设置只读


从的配置文件添加以下记录,即可:
    slaveof 1.1.1.1 3306 

sentinel,哨兵

启动主redis:
    redis-server /etc/redis-6379.conf  启动主redis
    redis-server /etc/redis-6380.conf  启动从redis
    
在linux中:
    找到 /etc/redis-sentinel-8001.conf  配置文件,在内部:
        - 哨兵的端口 port = 8001
        - 主redis的IP,哨兵个数的一半/1
    
    找到 /etc/redis-sentinel-8002.conf  配置文件,在内部:
        - 哨兵的端口 port = 8002
        - 主redis的IP, 1 

    启动两个哨兵

redis-cluster集群方案

redis集群、分片、分布式redis        
redis-py-cluster

问题:

目前你们公司项目1000用户,QPS=1000 ,如果用户猛增10000w?
项目如何提高的并发?

1. 数据库读写分离
2. 设置缓存
3. 负载均衡

用redis做过什么

  -配合django做过缓存,将常用且不易修改的数据放进来(博客)

  -购物车信息

  -Session

    -缓存配置文件
    -session配置文件中指定使用缓存
  -rest api中访问频率控制

  -基于flask、websocket实现的投票系统(redis做消息队列)

  -scrapy中

    - 去重规则
    - 调度器:先进先出、后进先出、优先级队列
    -pipeline
    -起始url
  - 商品热点信息

  - 计数器
  - 排行(有序集合)

为什么redis要做主从复制

目的是对redis做高可用,为每一个redis实例创建一个备份称为slave,让主和备之间进行数据同步,save/bsave。
主:写
从:读
优点:
- 性能提高,从分担读的压力。
- 高可用,一旦主redis挂了,从可以直接代替。

存在问题:当主挂了之后,需要人为手工将从变成主。

redis的sentinel是什么?

帮助我们自动在主从之间进行切换
检测主从中 主是否挂掉,且超过一半的sentinel检测到挂了之后才进行进行切换。
如果主修复好了,再次启动时候,会变成从。

redis的cluster是什么?

集群方案:
    - redis cluster 官方提供的集群方案。
    - codis,豌豆荚技术团队。
    - tweproxy,Twiter技术团队。
redis cluster的原理?
    - 基于分片来完成。
    - redis将所有能放置数据的地方创建了 16384 个哈希槽。
    - 如果设置集群的话,就可以为每个实例分配哈希槽:
        - 192.168.1.200-5000- 192.168.1.215001-10000- 192.168.1.2210001-16384- 以后想要在redis中写值时,
        set k1 123 
      将k1通过crc16的算法,将k1转换成一个数字。然后再将该数字和16384求余,如果得到的余数 3000,那么就将该值写入到 192.168.1.20 实例中。

redis是否可以做持久化?

RDB:每隔一段时间对redis进行一次持久化。
     - 缺点:数据不完整
     - 优点:速度快
AOF:把所有命令保存起来,如果想到重新生成到redis,那么就要把命令重新执行一次。
     - 缺点:速度慢,文件比较大
     - 优点:数据完整

redis的过期策略

voltile-lru:    从已设置过期时间的数据集(server.db[i].expires)中挑选最近频率最少数据淘汰
volatile-ttl:   从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰
volatile-random:从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰


allkeys-lru:       从数据集(server.db[i].dict)中挑选最近最少使用的数据淘汰
allkeys-random:    从数据集(server.db[i].dict)中任意选择数据淘汰
no-enviction(驱逐):禁止驱逐数据

redis的分布式锁实现。

- 写值并设置超时时间
- 超过一半的redis实例设置成功,就表示加锁完成。
- 使用:安装redlock-py 
from redlock import Redlock

dlm = Redlock(
    [
        {"host": "localhost", "port": 6379, "db": 0},
        {"host": "localhost", "port": 6379, "db": 0},
        {"host": "localhost", "port": 6379, "db": 0},
    ]
)

# 加锁,acquire
my_lock = dlm.lock("my_resource_name",10000)
if  my_lock:
    # J进行操作
    # 解锁,release
    dlm.unlock(my_lock)
else:
    print('获取锁失败')

 

posted @ 2018-05-05 09:39  小杜要加油  阅读(246)  评论(0)    收藏  举报