python面试常问

一、Python基础部分

1. 数据类型

  1. 数字类型(Numbers): 整数(int), 浮点数(float), 复数(complex)

  2. 布尔(Booleans): True和False

  3. 字符串(Str):Uniconde字符序列, 在引号内包含

  4. 列表(list): 有序的值的序列

  5. 元组(Tuples): 有序的值的序列且不可改变

  6. 字典(Dictionaries): 无序的键值对的组合

  7. 集合(Sets): 无序的不含重复值的序列

2.字符串操作

- join()  字符串拼接
- index()/rindex()
- find()/rfind()
- title()
- upper()/lower()
- split() 默认一个或多个空格分隔
- capitalize() 首字母大小
- rjust(width, pad_str)/ljust()
- encode()
- startswith()/endswith()
- replace()/strip()
- isdigit()/isalpha()/isalnum()/isupper()/islower()/istitle()
- format()
- count('c')

3. 列表操作

1、列表添加元素(“增”append,extend,insert) 
2、修改元素(通过修改已存在元素下标对应的值来进行修改元素)
3、查找元素(in,not in,index,count),查看指定的元素是否存在 
4、删除元素(del,pop,remove)
5、排序(sort,reverse) 
3.1 一次遍历获取列表中第二大的值
list1 = [2, 3, 45, 6, 7, 83, 4, 56, 7]
max_value = list1[0]
second_value = list1[0]
for i in list1:
    if i > max_value:
        max_value = i
    elif second_value < i < max_value:
        second_value = i
print(second_value) # 56

4. 列表添加元素,append和extend之间的区别

a. append可以追加任意类型的元素,而extend只能追加可迭代对象
b. 当添加可迭代对象的时候,append会将整个可迭代对象添加进去,但是extend只会添加可迭代对象中元素(打碎加入到列表末尾)
c. 二者都是将元素追加到列表的末尾
d. 二者都是在原列表的基础上进行添加的

5. 深拷贝和浅拷贝

这个可以从赋值开始说,

赋值(即=):数据完全共享,不同的变量指向同一块内存地址

  • 可变数据类型 ----> 修改其中一个,另一个也随之改变
  • 不可变数据类型 ----> 修改了其中一个,另一个并不会改变

浅拷贝:数据半共享(拷贝其数据另外开辟内存存放,但是只拷贝成功第一层)

  • 可变数据类型 ----> 修改其中一个,另一个也随之改变
  • 不可变数据类型 ----> 修改了其中一个,另一个并不会改变
a = [2,3,4,[5,6,7]]
b = a.copy()
print(a,id(a))  # [2, 3, 4, [5, 6, 7]] 2496163828808
print(b,id(b))  # [2, 3, 4, [5, 6, 7]] 2496168588808

a[2] = 9
print(a,id(a))  # [2, 3, 9, [5, 6, 7]] 2496163828808
print(b,id(b))  # [2, 3, 4, [5, 6, 7]] 2496168588808

a[3][0]='x'
print(a,id(a))  # [2, 3, 9, ['x', 6, 7]] 2496163828808
print(b,id(b))  # [2, 3, 4, ['x', 6, 7]] 2496168588808

在这里插入图片描述

深拷贝:数据不共享(拷贝其数据完全独立存放另一内存空间,两份数据之间互不影响)

import copy
a = [2,3,4,[5,6,7]]
b = copy.deepcopy(a)
print(a,id(a))  # [2, 3, 4, [5, 6, 7]] 2235721617032
print(b,id(b))  # [2, 3, 4, [5, 6, 7]] 2235721617096

a[2] = 9
print(a,id(a))  # [2, 3, 9, [5, 6, 7]] 2235721617032
print(b,id(b))  # [2, 3, 4, [5, 6, 7]] 2235721617096

a[3][0]='x'
print(a,id(a))  # [2, 3, 9, ['x', 6, 7]] 2235721617032
print(b,id(b))  # [2, 3, 4, [5, 6, 7]] 2235721617096

6. 创建字典方法

# 方式一
dict1 = {"name":"张三","age":10,"score":100}

# 方式二
dict2 = {}
dict2["name"] = "张三"
dict2["age"] = 10
dict2["score"] = 88

# 方式三
# dict(key=value....)
dict3 = dict(name="lisi",age=9,score=66)

# 方式四
# dict([(key1,value1),(key2,value2)....])
dict4 = dict([("one",10),("two",20),("three",30)])

# 方式五
# dict(zip(seq1所有的key,seq2所有的value)),seq1和seq2可以是列表,也可以是元组
dict5 = dict(zip(["111","222","333"],[1,2,3]))

dict5 = dict(zip(["111","222","333","444"],[1,2,3]))
print(dict5)  # {'111': 1, '222': 2, '333': 3}

7. dict()和fromkeys的区别

d2 = dict(zip([22,33],["a","b"]))
print(d2)  #{22: 'a', 33: 'b'}

d2 = dict.fromkeys([22,33],["a","b"])
print(d2)  #{22: ['a', 'b'], 33: ['a', 'b']}

8. 字典合并方式

(1)update 内置方法
user = {"name":"xiao", "age":22}
gen = {"gender":"male"}
user.update(gen)
print(user)  # {'name': 'xiao', 'age': 22, 'gender': 'male'}
print(gen)   # {'gender': 'male'}

(2)dict.items()合并。 items方法将dict转成 dict_items,再对这两个dict_items取并集,利用 dict函数,转成字典。
aaa = {"name":"xiao", "age":22}
title = {"title":"hello world"}
data = dict(aaa.items()|title.items())
print(aaa.items())  # dict_items([('name', 'xiao'), ('age', 22)])
print(data)  # {'age': 22, 'name': 'xiao', 'title': 'hello world'}

(3)字典解析式
aaa = {"name":"xiao", "age":22}
title = {"title":"hello world"}
data = {k:v for d in [aaa,title] for k,v in d.items()}
print(data)  # {'name': 'xiao', 'age': 22, 'title': 'hello world'}

(4)itertools字典是可迭代对象,可以使用 itertools.chain() 函数先将多个字典串联起来,然后组成一个更大的可迭代对象,在用dict 转成字典。
import itertools
aaa = {"name":"xiao", "age":22}
title = {"title":"hello world"}
print(dict(itertools.chain(aaa.items(),title.items())))  # {'name': 'xiao', 'age': 22, 'title': 'hello world'}
print(aaa.items())  # dict_items([('name', 'xiao'), ('age', 22)])
print(itertools.chain(aaa.items(),title.items()))  # <itertools.chain object at 0x000001F9E196DB88>

9. 字典交换Key和Value——字典推导式

dict21 = {value:key for key,value in dict1.items()}
print(dict21)

10. 可迭代对象和迭代器之间的区别和联系

可迭代对象:Iterable,可以直接作用于for循环的数据类型,如:list,tuple,dict,set,str,生成器等
迭代器:Iterator,可以直接作用于for循环或者可以通过next获取下一个元素的数据类型,如:生成器

联系:
迭代器一定是可迭代对象,可迭代对象不一定是迭代器
但是,可以通过iter()将不是迭代器的可迭代对象在转换为迭代器

11. 迭代器和生成器的区别

迭代是访问集合元素的一种方式。迭代器是一个可以记住遍历位置的对象.

一个实现了__iter__方法和__next__方法的对象,就是迭代器。

class MyIterator(object):
 	def __init__(self, n):
     	self.n = n
     	self.current = 0

 	# 自定义迭代器需要重写__iter__和__next__方法
 	def __iter__(self):
    	 return self

 	def __next__(self):
     	if self.current < self.n:
        	value = self.current
         	self.current += 1
         	return value
     	else:
         	raise StopIteration

my_it = MyIterator(10)
for i in my_it:    # 迭代器重写了__iter__方法,它本身也是一个可迭代对象
 print(i)

迭代器核心功能:

​ 可以通过next()函数的调用来返回下一个数据值。如果每次返回的数据值不是在一个已有的数据集合中读取的,而是通过程序按照一定的规律计算生成的,那么也就意味着可以不用再依赖一个已有的数据集合,也就是说不用再将所有要迭代的数据都一次性缓存下来供后续依次读取,这样可以节省大量的存储(内存)空间。

例—> 斐波那契数列:

class FibIterator(object):
    """斐波那契数列迭代器"""
    def __init__(self, n):
        """
        :param n: int, 指明生成数列的前n个数
        """
        self.n = n
        # current用来保存当前生成到数列中的第几个数了
        self.current = 0
        # num1用来保存前前一个数,初始值为数列中的第一个数0
        self.num1 = 0
        # num2用来保存前一个数,初始值为数列中的第二个数1
        self.num2 = 1

    def __next__(self):
        """调用next()函数来获取下一个数"""
        if self.current < self.n:
            num = self.num1
            self.num1, self.num2 = self.num2, self.num1+self.num2
            self.current += 1
            return num
        else:
            raise StopIteration

    def __iter__(self):
        """迭代器的__iter__返回自身即可"""
        return self

if __name__ == '__main__':
    fib = FibIterator(10)
    for num in fib:
        print(num, end=" ")

(2)生成器

生成器是一类特殊的迭代器

生成器实现方式:

  • 列表推导式[] —> ()
  • 函数结合yield

在使用生成器实现的方式中,我们将原本在迭代器__next__方法中实现的基本逻辑放到一个函数中来实现,但是将每次迭代返回数值的return换成了yield,此时新定义的函数便不再是函数,而是一个生成器了。

简单来说:只要在def中有yield关键字的 就称为 生成器

yield关键字有两点作用:

  • 保存当前运行状态(断点),然后暂停执行,即将生成器(函数)挂起
  • 将yield关键字后面表达式的值作为返回值返回,此时可以理解为起到了return的作用

可以使用next()函数让生成器从断点处继续执行,即唤醒生成器(函数)

(3)区别:
①生成器是一类特殊的迭代器
②迭代器是一个对象,生成器是一个函数

11.列表生成式和列表推导式

列表生成式即推导式

列表生成式即List Comprehensions,是Python内置的非常简单却强大的可以用来创建list的生成式。
列表生成式的结构是在一个中括号里包含一个表达式,然后是一个for语句,然后是0个或多个for或者if语句。列表表达式可以是任意的,意思是你可以在列表中放入任意类型的对象。返回结果将是一个新的列表,在这个以if和for语句为上下文的表达式运行完成之后产生。

```python
# 普通生成列表
list1 = []
for i in range(1, 11):
    list1.append(i * i)
print(list1)

# 列表生成式
list2 = [i * i for i in range(1, 11)]
print(list2)

12. 高阶函数

概念:函数的一个参数是函数或者返回值是一个函数,都可以称为高阶函数。

(1)map()函数接收两个参数,一个是函数,一个是序列。map将传入的函数依次作用到序列的每个元素,并把结果作为新的序列返回。

(2)reduce():接收两个参数,把函数作用在一个序列上,reduce把 结果继续和序列下一个元素做累积计算。

def fun():
    return x*10+y

print(reduce(fun,[1,2,3,4,5]))

# 输出结果:
12345

(3)filter()过滤函数:接收一个函数和一个序列,filter函数把传入的函数依次作用于每个元素,然后根据返回值
是True或False来决定保留或者丢弃该元素。

(4)sorted():接收一个序列和一个函数进行进行排序。

13. 装饰器

概念: 在Python中,不改变已有函数代码的前提下,给函数增加新功能的一种函数。它的实质也是一种高阶函数。

装饰器的使用场景:

插入日志、性能测试、事务处理、缓存、权限校验等场景

  • 收集函数的操作或错误日志记录
  • 验证函数的使用权限
  • 计算函数的运行时间
  • 在ORM/DB模型操作时,通过属性方法动态地获取关联的数据
  • 函数数据的缓存
  • 定制函数的输入和输出(序列化和反序列化)
# 需求:书写一个装饰器,可以统计任意一个函数的执行时间
import time

def get_time(func):
 def inner(*args,**kwargs):
     # 开始时间
     start = time.time()   #获取时间差
     #调用原函数
     func(*args,**kwargs)
     # 结束时间
     end = time.time()
     result = round(end - start,3)
     return result
 return inner

@get_time
def check():
 for i in range(1000000):
     pass
print(check())

简单装饰器:

import logging

def use_logging(func):
    def wrapper():
        logging.warning("%s is running" % func.__name__)
        return func()
    return wrapper

@use_logging
def func():
    print("i am func")

func()

# 输出
i am foo
WARNING:root:func is running

被装饰函数有参数

import logging

def use_logging(func):
    def wrapper(*args, **kwargs):
        logging.warning("%s is running" % func.__name__)
        return func(*args, **kwargs)
    return wrapper

@use_logging
def func(x):
    print("i am func,i have %s" % x)

func('参数')

# 输出
WARNING:root:func is running
i am func,i have 参数

带参数的装饰器

def deco_para(parameter):  # 接收装饰器参数
    def deco_func(func):  # 接收被装饰函数
        def wrapper(*args, **kwargs):  # 接收被装饰函数参数
            print(parameter)
            func(*args, **kwargs)
            print('看看打印顺序')
        return wrapper
    return deco_func

@deco_para('i am decorator arguments')
def foo(x):
    print('i am foo, i have %s' % x)

if __name__ == '__main__':
    foo('参数')

Python内置的装饰器:

  • @property/@name.setter 属性方法
  • @classmethod
  • @staticmethod
  • @asyncio.coroutine
  • @abc.abstractmethod
  • @abc.abstractclassmethod
  • @abc.abstractstaticmethod
  • @classonlymethod

14. Python的垃圾回收机制

Python中存在三种内存回收机制:

引用计数 标记清除 分代回收

在python中维护了一个refchain的双向环状链表,这个链表存储程序中创建的所有对象,每种类型的对象都有一个 ob_refcnt 的引用计数器,对象被引用,则计数器的值 +1,引用被删除则计数器的值 -1,最后引用计数的值为 0 时,会进行垃圾回收(对象销毁、从refchain中移除),

但是,在python中对于那些可以有多个元素组成的对象可能会存在循环引用问题,为了解决这个问题python引入了 标记清除 ,在其内部再维护了一个链表,专门放那些可能存在循环引用的对象 (list/tuple/dict/set), 某种情况下 触发,会去扫描 可能存在循环引用的链表中的每一个元素,检查是否有循环引用,如果有则双方的引用计数器 -1,如果是 0 则垃圾回收,

然而,又有一个新的问题产生,就是什么时候扫描?可能存在循环引用的链表扫描代价较大,每次扫描耗时久,所以又引入了 分代回收 ,将可能存在循环引用对象维护成 3 个链表,分别是 0代,1代,2代,所有可能存在循环引用的对象都存储在 0代链表,当对象个数达到700个的时候扫描一次,是垃圾则回收,不是则移入 1代,依次类推,0代扫描10次,1代扫描一次,1代扫描10次,2代扫描1次。

15. 匿名函数,优缺点

匿名函数: 一个lambda表达式,本质上还是一个函数,可以设置参数,也可以调用;表达式本身就是函数的 运算结果
优点:
简化代码
减少内存空间的使用
缺点:
只能实现简单的逻辑,逻辑一旦复杂,代码的可读性会降低,则不建议使用

16. 类属性(字段)和对象属性(字段)的区别和联系

a. 定义位置不同:类属性直接定义在类中,对象属性:对象动态绑定或者定义在构造函数中
b. 访问方式不同:类属性可以通过对象或者类访问,对象属性只能通过对象访问
c. 在内存中出现的时机不同:类属性随着类的加载出现在内存中,对象属性随着对象的创建出现在内存中,
类属性优先于对象属性出现在内存中
d. 优先级不同:当类属性和对象属性重名的情况下,对象属性的优先级高于类属性
e. 使用场景不同:是多个对象共享的数据则定义为类属性,如果是各个对象不同的数据则定义为对象属性

17. 普通函数和__init__的区别和联系

不同点:
a. 函数名不同:普通函数的函数名可以自定义,但是,__init__是固定的,不能随意更改。
b. 调用不同:普通函数必须手动调用,__init__是在创建对象的过程中自动调用的。
c. 调用的次数:对于同一个对象而言,__init__只会被调用一次,但是,普通函数可以被无数次调用。
相同点:
a. 定义在类中,形参列表的第一个参数为self。都被称为实例函数。
b. 形参列表仍然可以设置默认参数,不定长参数,实参列表也可以设置关键字参数。
c. 也可以设置返回值。

18. 实例函数,类函数和静态函数区别和联系

相同点:
本质都是函数,所以默认参数,关键字参数,不定长参数都可以正常使用,也可以设置返回值
不同点:
a. 是否有装饰器:类函数需要被@classmethod修饰,静态函数需要被@staticmethod修饰,实例函数不需要任何装饰器
b. 参数列表:类函数的第一个参数必须为cls,实例函数的第一个参数必须为self,静态函数的参数没有要求
c. 调用方式:类函数和静态函数可以通过类名或者对象调用,实例函数只能通过对象调用
d. 使用场景不同:
静态函数(@staticmethod): 即静态方法,主要处理与这个类的逻辑关联, 如验证数据;
类函数(@classmethod):即类方法, 更关注于从类中调用方法, 而不是在实例中调用方法, 如构造重载;
成员函数: 实例的方法, 只能通过实例进行调用;

19. 线程和进程的区别

  • 一个运行程序就是一个进程,一个进程至少包含一个线程,进程是由操作系统分配资源,进程之间的内存是相互独立的,如果需要在多个进程间通信的话,可以使用管道/队列/共享内存/信号/linux socket

  • 线程是属于某一个进程的,线程是应用程序中工作的最小单元,而属于同一个进程多个线程之间的内存是共享的,那么就会出现内存资源的安全问题,python为了线程安全,就设置了全局解释器锁(GIL)机制,既一个进程中同时只能有一个线程访问cpu

全局解释器锁(GIL)
同一进程中假如有多个线程运行,一个线程在运行python程序时会霸占python解释器(加一把锁,即GIL),使该进程内的其他线程无法运行,等该线程运行完后,其他线程才能运次行。如果线程运行过程中出现耗时操作,则解释器锁打开,使其他线程运行,所以在多进程中线程的运行仍是有先后顺序的,并不是同时运行。

Lock和Rlock区别:

由于线程之间是进行随机调度,并且每个线程可能只执行n条执行之后,当多个线程同时修改同一条数据时可能会出现脏数据,所以,出现了线程锁 - 同一时刻允许一个线程执行操作。

  • acquire  给线程上锁
  • release  给线程解锁

img  img

无论是lock还是rlock,提供的方法都非常简单,acquire和release。但是rlock和lock的区别是什么呢?**RLock允许在同一线程中被多次acquire。而Lock却不允许这种情况。**注意:如果使用RLock,那么acquire和release必须成对出现,即调用了n次acquire,必须调用n次的release才能真正释放所占用的锁。

20. 协程 asyncio

正是由于python多线程的缺陷,需要引入协程的概念。

协程是一种用户态的轻量级线程,线程和进程由CPU调度, 协程由用户(开发者或程序本身)来调度的。
协程存在的意义:对于多线程应用,CPU通过切片的方式来切换线程间的执行,线程切换时需要耗时(保存状态,下次继续)。协程,则只使用一个线程,在一个线程中规定某个代码块执行顺序。

协程的适用场景:当程序中存在大量不需要CPU的操作时(IO),适用于协程;

协程的发展阶段:
- 第一阶段: yield和send的生成器的关键字来实现用户自我调度
- 第二阶段(python3.4): @asyncio.coroutine 和 yield from
- 第三阶段(python3.5): async 和 await 两个关键

【注】第二和第三阶段使用asynio模块, 协程的对象必须事件循环对象中运行。

- 使用asyncio模块
- 函数上方使用@asyncio.coroutine或async关键字,使得变成协程对象
- 在协程函数中,使用yield from 或 await关键字,告知事件模型当前协程等待其它协程完成任务
- 协程对象需要在事件模型中运行
	- loop = asyncio.get_event_loop() 获取事件模型对象
	- loop.run_until_complete(协程对象)
			- 协程对象,默认是单任务
			- 多个协程对象,可以通过列表方式和asynio.wait()函数封装多任务协程对象

21.创建线程的方法

Python3.x通过Threading模块创建新的线程有两种方法:

  1. 通过threading.Thread(target=executable Method),即通过传递给Thread对象一个可执行方法或对象;

  2. 通过继承 threading.Thread 定义子类创建多线程
    使用 threading 模块创建多线程,直接从 threading.Thread 继承,然后重写__init__() 方法和 run() 方法

    import threading
    import time
    
    class MyThread(threading.Thread):   #继承父类threading.Thread
        def __init__(self, threadID, name, counter):
            threading.Thread.__init__(self)
            self.threadID = threadID
            self.name = name
            self.counter = counter
    
        def run(self):    #把要执行的代码写到run函数里面,线程在创建后会直接运行run
            print("开始运行:" + self.name)
            print(self.name, self.counter, 5)
            print("结束运行:" + self.name)
    
    
    def print_time(threadName, delay, counter):
        while counter:
            time.sleep(delay)
            print("%s 进程 在:" % (threadName, time.ctime(time.time())))
            counter -= 1
    
    
    #创建新线程
    thread_1 = MyThread(1, "Thread-1", 1)
    thread_2 = MyThread(2, "Thread-2", 2)
    
    #开启线程
    thread_1.start()
    thread_2.start()
    
    #等待线程结束
    thread_1.join()
    thread_2.join()
    

    应用场景

    • 做登录业务时使用到sms短信网关业务,创建子线程,让子线程去调用sms服务,主线程则返回,这样做可以提高用户的体验度
    • 多线程爬虫,有些时候,比如下载图片,因为下载图片是一个耗时的操作。如果采用之前那种同步的方式下载。那效率肯会特别慢。这时候我们就可以考虑使用多线程的方式来下载图片。
    • Redis 默认rdb持久化时,为了性能最大化,让子进程来完成写操作,让主进程继续处理命令,所以是 IO 最大化。使用单独子进程来进行持久化,主进程不会进行任何 IO 操作,保证了 redis 的高性能

22. 线程本地变量和条件变量

线程本地变量: 
 因为多线程是共享同一个进程的内存,如果数据仅用于当前线程时,可以基于threading.local类实现线程私有数据。
 
线程条件变量: 
  多线程共同修改某一个数据对象时,为了使得数据更安全(一致性),可以使用 threading.Condition条件变量类。它具有对共享数据的加锁、解锁功能,也可以在数据不满足条件时,让当前线程挂载;反之,满足条件时,可以唤醒其它挂起的线程。

23. 多任务的4种实现方式

a.多进程模式:启动多个进程,每个进程虽然只有一个线程,但多个进程可以一块执行多个任务
b.多线程模式:启动一个进程,在一个进程内启动多个线程,这样,多个线程也可以一块执行多个任务
c.协程模式:启动一个进程,启动一个线程,但是借助于函数生成器【yield】可以执行多个子任务
d.多进程+多线程模式:启动多个进程,每个进程再启动多个线程,这样同时执行的任务就更多了

24. 创建进程的方法

1. os.fork创建,      只支持Linux/Unix系统
2. os.system(cmd)    创建子进程来执行cmd描述的操作系统命令,但无法获取命令执行的结果
3. os.popen(cmd)     创建子进程来执行cmd命令,可以获取命令执行的结果。引用subprocessing.Popen类。
4. multiprocssing.Process类创建子进程,需要重写run方法,来实现自己的业务。

25. fork的优缺点

fork的缺点:
a. 兼容性差:只能在Linux下使用,Windows系统无法使用
b. 扩展性差:当创建多个进程对象的时候,管理较为复杂
c. 会产生“孤儿”进程或者“僵尸”进程,需要手动回收系统资源
fork的优点:
是系统自带的更加接近底层的创建进程的方式,运行效率高

26. 文件操作Open

默认打开模式:  "r" 只读-权限,  "t" 文本-文件类型
常用模式的组合: 
	  r+  读写文本,如果文件不存在,则会报错(异常)
	  r+b 读写字节数据(媒体文件: 图片、音频、视频、office文件), 如果文件不存在,则报错
	  w+  读写文本, 如果文件不存在,则会自动创建
	  a+/a+b  追加文本或字节数据,如果文件不存在,则会创建。
	  
encoding参数:  只针对读取文本数据模式, 即指定文本数据的编码(名词), 如果是"b"模式时,则不需要指定这个参数,如果指定则会抛出异常。

27. with上下文

with 是Python中管理上下文环境时的关键字, 当某一个对象在使用with时,则会存在两个节点,即当对象进入上下文时,则调用对象的__enter__()方法,此方法返回一个对象,在with表达式中,通过as关键字来接收这个对象。当代码执行完,则会退出上下文,此时会调用对象的__exit__()方法。

28. 单例 Singleton

解决问题: 无论类实例化多少次,只有一个实例对象。

class Singleton:
    def __init__(self, name, phone):
       self.name = name
       self.phone = phone
    def save(self):
        print(self.name, self.phone)
   
    def __new__(cls, *args, **kwargs):
        if not hasattr(cls, "instance"):
           cls.instance = super().__new__(cls)
        return cls.instance
s1 = Singleton("disen", "17791692095")
s2 = Singleton("jack", "17791692094")
s3 = Singleton("lucy", "17791692093")

s1.save()
s2.save()
s3.save()

29. __init__,__new____call__三者的区别

__init__ 是初始化实例对象的,是__new__返回对象之后调用的。
__new__  是创建实例对象的,它是一个静态方法。调用父类时,只传入一个cls对象即可。用来分配内存空间,它的返回值传递给__init__的第一个参数self
__call__ 当实例对象被作为函数调用时,对象会调用__call__()用法,这种机制一般会用于装饰器类中。

30. logging日志模块的常用Handler处理器类

日志是用于记录(Logger)程序中的一些重要信息的,记录的信息按等级(Level)交给特定的处理器Handler按一定的格式(Formatter)进行处理(打印、文件保存、上传网络和发送邮件等)。

- logging.StreamHandler 输出流
- logging.FileHandler   输出到文件
- logging.handlers.SMTPHandler  通过SMTP邮件协议,向指定的邮箱发送日志信息
- logging.handlers.HTTPHandler  通过HTTP Web协议,向指定WEB服务器上传日志信息
- logging.handlers.TimedRotatingFileHandler 时间分隔的日志文件处理器。

31. re模块中常用的函数:

  • compile(pattern, flags) 生成正则对象,一次生成对象,可以被多次调用。
  • match(pattern, string, flags=0) 如果pattern正则表达式匹配了string内容,则返回match对象,反之返回None。一般用于验证string的完整性(手机号、身份证、邮箱)。
  • search(pattern, string, pos=0, flags=0) 默认从pos的索引下标开始, 匹配第一次的内容
  • findall(pattern, string, flags=0) 查找所有与正则匹配的数据,返回list。
  • sub(pattern, replace_str, string, flags=0) 在string文本中,使用replace_str替换正则匹配的内容

32. 正则的转义字符

\d 任意一个数字 , 表示[0-9]
\D 非任意一个数字
\w 任意一个数字、字母、下划线或中文【Python中】 ,表示 [a-zA-Z0-9_]
\W 非任意一个数字、字母、下划线或中文
\s 任意一个空白(<Space>空格、<Tab>制表符、<Enter>换行符等)
\S 非任意一个空白

33. 常用魔术方法

魔术方法就是一个类/对象中的方法,和普通方法唯一的不同时,普通方法需要调用!而魔术方法是在特定时刻自动触发。

1.__init__

初始化魔术方法
触发时机:初始化对象时触发(不是实例化触发,但是和实例化在一个操作中)
参数:至少有一个self,接收对象
返回值:无
作用:初始化对象的成员
注意:使用该方式初始化的成员都是直接写入对象当中,类中无法具有

2.__new__

实例化魔术方法
触发时机: 在实例化对时触发
参数:至少一个cls 接收当前类
返回值:必须返回一个对象实例
作用:实例化对象
注意:实例化对象是Object类底层实现,其他类继承了Object的__new__才能够实现实例化对象。
没事别碰这个魔术方法,先触发__new__才会触发__init__ 

3.__del__

析构魔术方法
触发时机:当对象没有用(没有任何变量引用)的时候被触发
参数:一个self 结婚搜对象
返回值:无
作用:使用完对象是回收资源
注意:del不一定会触发当前方法,只有当前对象没有任何变量接收时才会触发

4.__call__

调用对象的魔术方法
触发时机:将对象当作函数调用时触发 对象()
参数:至少一个self接收对象,其余根据调用时参数决定
返回值:根据情况而定
作用:可以将复杂的步骤进行合并操作,减少调用的步骤,方便使用
注意:无

5.__len__

触发时机:使用len(对象) 的时候触发
参数:一个参数self
返回值:必须是一个整型
作用:可以设置为检测对象成员个数,但是也可以进行其他任意操作
注意:返回值必须必须是整数,否则语法报错,另外该要求是格式要求。

6.__str__

触发时机:使用print(对象)或者str(对象)的时候触发
参数:一个self接收对象
返回值:必须是字符串类型
作用:print(对象时)进行操作,得到字符串,通常用于快捷操作
注意:无

7.__repr__

触发时机:在使用repr(对象)的时候触发
参数:一个self接收对象
返回值:必须是字符串
作用:将对象转使用repr化为字符串时使用,也可以用于快捷操作

repr函数和str函数处理字符串只有一个区别:

str的结果 字符串本身 (结果可以被eval执行)

如:x = ‘无图言Diao’ str() ->无图言Diao

rerpr的结果 字符串定义结构 (eavl不会执行repr结果)

如:x = ‘无图言Diao’ repr() ->‘无图言Diao’

备注: 在类中通常情况下__str__和__repr__ 设置相同即可

eval()

函数 将字符串当作python代码执行

格式:eval(字符串)

返回值:可以有返回值

8.__bool__

触发时机: 使用bool(对象)的时候触发
参数:一个self接收对象
返回值:必须是布尔值
作用:根据实际情况决定,可以作为快捷方式使用
注意:仅适合于返回布尔值的操作

9.__format__

触发时机:使用字符串.format(对象)时候触发
参数:一个self接收对象,一个参数接收format的{}中的格式,例如:>5
返回值:必须是字符串
作用:设置对象可以作为format的参数,并且自定义对象格式化的规则
注意:无

二、网络协议

1. TCP和UDP之间的区别

TCP: Transmission Control Protocol 传输控制协议,基于字节流的传输层通信协议
a. 可靠的【确保接收方完全正确地获取发送方所发送的全部数据】
b. 面向连接的【面向连接的协议,数据传输必须建立连接,所以TCP需要连接时间】要任何装饰器
c. 数据传输的效率较低
d. 传输数据大小限制,一旦连接建立,双方可以按统一的格式传输大的数据

UDP: Transmission Control User Datagram Protocol的简称,用户数据包协议,提供面向事务的简单不可靠信息传送服务
a. 不可靠的【所发送的数据报并不一定以相同的次序到达接收方,UDP将数据包发送给对方,对方不一定能接受到】,比如:飞秋
b. 无连接的【每个数据报中都给出了完整的地址信息,因此无需要建立发送方和接收方的连接】
c. 效率高,速度快
d. UDP传输数据时是有大小限制的,每个被传输的数据报必须限定在64KB之内

2. TCP/IP协议三次握手和四次挥手

三次握手:

概念:指在发送数据的准备阶段,服务器和客户端之间需要三次
第一次握手:建立连接时,客户端向服务器发送一个SYN包,并进入SYN_SENT状态,等待服务器确认
第二次握手:当服务器收到客户端的请求后,此时要给客户端给一个确认信息ACK,同时发送SYN包,此时服务器进入 SYN_RECV状态
第三次握手:客户端收到服务器发的ACK+SYN包后,向服务器发送ACK,发送完毕之后,客户端和服务器进入 ESTABLISHED(TCP连接成功)状态,完成三次握手

socket里面的客户端的connect + 服务端的accept 就实现了三次握手

四次挥手:

概念:四次挥手就是说关闭TCP连接的过程,当断开一个TCP连接时,需要客户端和服务器共发送四个包确认
第一次挥手:客户端发送一个FIN,用来关闭客户端到服务器的数据传输,客户端进入FIN_WAIT_1状态
第二次挥手:服务器收到FIN后,发送一个ACK给客户端,确认序号为收到序号+1(与SYN相同,一个FIN占用一个序 号),服务器进入CLOSE_WAIT状态
第三次挥手:服务器发送一个FIN,用来关闭服务器到客户端的数据传输,服务器进入LAST_ACK状态
第四次挥手:客户端收到FIN后,客户端进入TIME_WAIT状态,接着发送一个AKC给服务器,确认序号为收到序号+1,服务器进入CLOSED状态,完成四次挥手

socket里面的客户端的colse和服务端(conn)的colse实现四次挥手

3. socket

所有的Web应用本质上就是一个socket服务端,而用户的浏览器就是一个socket客户端,基于请求做出响应,客户都先请求,服务端做出对应的响应,按照http协议的请求协议发送请求,服务端按照http协议的响应协议来响应请求,这样的网络通信,我们就可以自己实现Web框架了。

通过对socket的学习,我们知道网络通信,我们完全可以自己写了,因为socket就是做网络通信用的,下面我们就基于socket来自己实现一个web框架,写一个web服务端,让浏览器来请求,并通过自己的服务端把页面返回给浏览器,浏览器渲染出我们想要的效果

在这里插入图片描述

socket参数的详解:

socket.socket(family=AF_INET,type=SOCK_STREAM,proto=0,fileno=None)

# 创建socket对象的参数说明:
family地址系列应为AF_INET(默认值),AF_INET6,AF_UNIX,AF_CAN或AF_RDS。 (AF_UNIX 域实际上是使用本地 socket 文件来通信)
type套接字类型应为SOCK_STREAM(默认值),SOCK_DGRAM,SOCK_RAW或其他SOCK_常量之一。 SOCK_STREAM 是基于TCP的,有保障的(即能保证数据正确传送到对方)面向连接的SOCKET,多用于资料传送。 SOCK_DGRAM 是基于UDP的,无保障的面向消息的socket,多用于在网络上发广播信息。
proto协议号通常为零,可以省略,或者在地址族为AF_CAN的情况下,协议应为CAN_RAW或CAN_BCM之一。
fileno如果指定了fileno,则其他参数将被忽略,导致带有指定文件描述符的套接字返回。 与socket.fromfd()不同,fileno将返回相同的套接字,而不是重复的。 这可能有助于使用socket.close()关闭一个独立的插座。

server.py

import socket

sk = socket.socket()          # 创建一个server端的对象
sk.bind(('127.0.0.1',9001))  # 给server端绑定一个地址
sk.listen()                   # 开始监听(可以接收)客户端给我的连接了

conn,addr = sk.accept()  # 建立连接 conn是连接
conn.send(b'hello')     # 发送消息
msg = conn.recv(1024)  # 接收消息
print(msg)
conn.close()     # 关闭连接

sk.close()

client.py

import socket

sk = socket.socket()
sk.connect(('127.0.0.1',9001))

msg = sk.recv(1024)
print(msg)
sk.send(b'byebye')

sk.close()

4. 简述HTTP协议的Content-Type、Content-Length、Content-Disposition

三者都是HTTP协议的报文头的属性,分别代表:
- Content-Type:  报文中body的数据类型,常见有text/html、text/css、image/png、application/json、application/javascript、application/x-www-form-urlencoded和multi/form-data等。
- Content-Length: 报文body的数据字节长度。
- Content-Disposition: 响应报文的弹出窗口的附加信息,如下载文件时,显示文件的名称等。一般同Content-Type为application/octet-stream组合使用,它的value为'attachment;filename=xxx.png'

5. C10K问题及解决方案

C10K:  高并发问题, 即在某一个时间段内,同时存在至少1万个任务需要处理。在HTTP协议中,由于受到响应时间的限制(用户体验),因此存在高并发问题。

解决方案: 
  1.Celery + 消息队列(Redis/RabbitMQ/Kafka)
  2. 自定义后台进程+消息队列 

6. Session与Cookie的区别

由于HTTP协议是无状态的,Cookie是前端(如浏览器)存储数据的技术;

浏览器访问服务端,带着一个空的cookie,然后由服务器产生内容,浏览器收到相应后保存在本地;

当浏览器再次访问时,浏览器会自动带上Cookie,这样服务器就能通过Cookie的内容来判断这个是“谁”了。

Session的技术基于Cookie,Session一般将当前(会话-连接)业务的数据,当客户端与服务器第一次连接时,服务端则会为当前的连接创建一个唯一标识(session_id/sessionid/jsessionid),并将唯一标识以Cookie的方式响应给客户端,客户端则默认存储。客户端在下一次发起请求时,默认将当前站点保存的Cookie附加到请求头上,向服务端发起请求。在服务端可以验证当前的SessionID,确认当前的连接是哪一个,并从中读取相关的数据(之前保存或操作的数据)。

如果前端将Cookie清空,则SessionID标识的会话连接则无效,当连接服务端会重新创建sessionId。如果前端禁用的Cookie功能,则无法创建持久连接,即Session功能无效。

7. 写出HTTP协议的请求与响应的报文第一行

请求报文:
GET path HTTP/1.1     GET请求方法; path是请求路径,默认是/; HTTP协议(HTTPs), 1.1版本号

响应报文:
HTTP/1.1  200 OK     ,如 HTTP/1.1  400 Bad Request

8. 跨域问题及解决的办法

跨域:  当前的请求目标(url)和当前的服务不属于同源(协议相同、host主机相同、端口相同)。
解决办法:
	1. 服务器端解决: 配置Options请求的响应头.  【建议解决】
   		Access-Control-Allow: *或白名单的IP地址
   		Access-Control-Headers: 可以发起的跨域请求头
   		Access-Control-Credentials: 是否支持安全请求
   		Access-Control-Methods: 允许请求的方法, 如Options/GET/POST/PUT/Delete
   
   		在Django中,使用django-cors-headers第三方库(插件配置)
   		在Flask, 使用flask-cors第三方库(修饰Flask的实例)
   
	2. 客户端解决办法: 代理(Nodejs可以配置 Proxy)
	3. 客户端jsonp

9. HTTP协议

HTTP协议是Hyper Text Transfer Protocol(超文本传输协议)的缩写,是用于从万维网(WWW:World Wide Web )服务器传输超文本到本地浏览器的传送协议。

HTTP是一个基于TCP/IP通信协议来传递数据(HTML 文件, 图片文件, 查询结果等)。

HTTP协议工作于客户端-服务端架构为上。浏览器作为HTTP客户端通过URL向HTTP服务端即WEB服务器发送所有请求。Web服务器根据接收到的请求后,向客户端发送响应信息。

无连接:无连接的含义是限制每次连接只处理一个请求。服务器处理完客户的请求,并收到客户的应答后,即断开连接。采用这种方式可以节省传输时间

无状态:HTTP协议是无状态协议。无状态是指协议对于事务处理没有记忆能力。缺少状态意味着如果后续处理需要前面的信息,则它必须重传,这样可能导致每次连接传送的数据量增大。另一方面,在服务器不需要先前信息时它的应答就较快。

9.1 请求方法:
常用:
GET(SELECT):从服务器取出资源(一项或多项)。(它本身不会对资源本身产生影响,因此满足  幂等性)
POST(CREATE):在服务器新建一个资源。 (它会对资源本身产生影响,每次调用都会有新的资源产生,因此  非幂等性)
PUT(UPDATE):在服务器更新资源(客户端提供改变后的完整资源)。(它直接把实体部分的数据替换到服务器的资源,多次调用它,只会产生一次影响,所以满足  幂等性)
PATCH(UPDATE):在服务器更新资源(客户端提供改变的属性)。(非幂等)
DELETE(DELETE):从服务器删除资源。 (调用一次和多次对资源产生影响是相同的,所以也满足  幂等性)

不常用:
HEAD:获取资源的元数据。
OPTIONS:获取信息,关于资源的哪些属性是客户端可以改变的(跨域时易见到)。

幂等性:

HTTP 幂等方法,是指无论调用多少次都不会有不同结果的 HTTP 方法。不管你调用一次,还是调用一百次,一千次,结果都是相同的
9.2 HTTP工作原理

HTTP协议定义Web客户端如何从Web服务器请求Web页面,以及服务器如何把Web页面传送给客户端。HTTP协议采用了请求/响应模型。

  • 客户端向服务器发送一个请求报文,请求报文包含:

    • 请求的方法、URL、协议版本、请求头部和请求数据。
  • 服务器以一个状态行作为响应,响应的内容包括:

    • 协议的版本、成功或者错误代码、服务器信息、响应头部和响应数据。

以下是 HTTP 请求/响应的步骤:

1、客户端连接到Web服务器

一个HTTP客户端,通常是浏览器,与Web服务器的HTTP端口(默认为80)建立一个TCP套接字连接。

2、发送HTTP请求

通过TCP套接字,客户端向Web服务器发送一个文本的请求报文,一个请求报文由请求行、请求头部、空行和请求数据4部分组成。

3、服务器接受请求并返回HTTP响应

Web服务器解析请求,定位请求资源。服务器将资源复本写到TCP套接字,由客户端读取。一个响应由状态行、响应头部、空行和响应数据4部分组成。

4、释放连接TCP连接

若connection 模式为close,则服务器主动关闭TCP连接,客户端被动关闭连接,释放TCP连接;若connection 模式为keepalive,则该连接会保持一段时间,在该时间内可以继续接收请求;

5、客户端浏览器解析HTML内容

客户端浏览器首先解析状态行,查看表明请求是否成功的状态代码。然后解析每一个响应头,响应头告知以下为若干字节的HTML文档和文档的字符集。客户端浏览器读取响应数据HTML,根据HTML的语法对其进行格式化,并在浏览器窗口中显示。

例如:在浏览器地址栏键入URL,按下回车之后会经历以下流程

1、浏览器向 DNS 服务器请求解析该 URL 中的域名所对应的 IP 地址;

2、解析出 IP 地址后,根据该 IP 地址和默认端口 80,和服务器建立TCP连接;

3、浏览器发出读取文件(URL 中域名后面部分对应的文件)的HTTP 请求;

4、服务器对浏览器请求作出响应,并把对应的 html 文本发送给浏览器;

5、释放 TCP连接;

6、浏览器将该 html 文本渲染并显示内容;

10. HTTP请求状态码表:

状态码状态码英文名称中文描述
100Continue继续,客户端应继续其请求
101Switching Protocols切换协议。服务器根据客户端的请求切换协议。只能切换到更高级的协议,例如,切换到HTTP的新版本协议
200OK请求成功。一般用于GET与POST请求
201Created已创建。成功请求并创建了新的资源
202Accepted已接受。已经接受请求,但未处理完成
203Non-Authoritative Information非授权信息。请求成功。但返回的meta信息不在原始的服务器,而是一个副本
204No Content无内容。服务器成功处理,但未返回内容。在未更新网页的情况下,可确保浏览器继续显示当前文档
205Reset Content重置内容。服务器处理成功,用户终端(例如:浏览器)应重置文档视图。可通过此返回码清除浏览器的表单域
206Partial Content部分内容。服务器成功处理了部分GET请求
300Multiple Choices多种选择。请求的资源可包括多个位置,相应可返回一个资源特征与地址的列表用于用户终端(例如:浏览器)选择
301Moved Permanently永久移动。请求的资源已被永久的移动到新URI,返回信息会包括新的URI,浏览器会自动定向到新URI。今后任何新的请求都应使用新的URI代替
302Found临时移动。与301类似。但资源只是临时被移动。客户端应继续使用原有URI
303See Other查看其它地址。与301类似。使用GET和POST请求查看
304Not Modified未修改。所请求的资源未修改,服务器返回此状态码时,不会返回任何资源。客户端通常会缓存访问过的资源,通过提供一个头信息指出客户端希望只返回在指定日期之后修改的资源
305Use Proxy使用代理。所请求的资源必须通过代理访问
306Unused已经被废弃的HTTP状态码
307Temporary Redirect临时重定向。与302类似。使用GET请求重定向
400Bad Request客户端请求的语法错误,服务器无法理解
401Unauthorized请求要求用户的身份认证
402Payment Required保留,将来使用
403Forbidden服务器理解请求客户端的请求,但是拒绝执行此请求
404Not Found服务器无法根据客户端的请求找到资源(网页)。通过此代码,网站设计人员可设置"您所请求的资源无法找到"的个性页面
405Method Not Allowed客户端请求中的方法被禁止
406Not Acceptable服务器无法根据客户端请求的内容特性完成请求
407Proxy Authentication Required请求要求代理的身份认证,与401类似,但请求者应当使用代理进行授权
408Request Time-out服务器等待客户端发送的请求时间过长,超时
409Conflict服务器完成客户端的 PUT 请求时可能返回此代码,服务器处理请求时发生了冲突
410Gone客户端请求的资源已经不存在。410不同于404,如果资源以前有现在被永久删除了可使用410代码,网站设计人员可通过301代码指定资源的新位置
411Length Required服务器无法处理客户端发送的不带Content-Length的请求信息
412Precondition Failed客户端请求信息的先决条件错误
413Request Entity Too Large由于请求的实体过大,服务器无法处理,因此拒绝请求。为防止客户端的连续请求,服务器可能会关闭连接。如果只是服务器暂时无法处理,则会包含一个Retry-After的响应信息
414Request-URI Too Large请求的URI过长(URI通常为网址),服务器无法处理
415Unsupported Media Type服务器无法处理请求附带的媒体格式
416Requested range not satisfiable客户端请求的范围无效
417Expectation Failed服务器无法满足Expect的请求头信息
500Internal Server Error服务器内部错误,无法完成请求
501Not Implemented服务器不支持请求的功能,无法完成请求
502Bad Gateway作为网关或者代理工作的服务器尝试执行请求时,从远程服务器接收到了一个无效的响应
503Service Unavailable由于超载或系统维护,服务器暂时的无法处理客户端的请求。延时的长度可包含在服务器的Retry-After头信息中
504Gateway Time-out充当网关或代理的服务器,未及时从远端服务器获取请求
505HTTP Version not supported服务器不支持请求的HTTP协议的版本,无法完成处理

三、MySQL相关知识

1. 数据库的认知

关系型数据库: SQLServer(字符集: GBK/GB2312)、MySQL(版本: 5.5, 5.6、5.7, 8.0)、Oracle(9i/10g/11g, 12c)、DB2、 MariDB(同MySQL是一个开发者)等。
非关系型数据库:  redis、mongodb(js)、elasticsearch 全文检索引擎(Lucene)
大数据库: hive、hbase、spark(Hadoop)

2.MySQL数据库支持的引擎

可以通过show engines语句查看MySQL支持的引擎
InnoDB:   支持事务、外键、行锁
MyISAM:   普通引擎,不支持事务和外键
Memory:   内存存储,无持久化
CSV:      csv格式存储数据, 体积小,文本数据

3. 事务特性-ACID

Atomicity(原子性):  事务中的所有操作要么都成功,要么都失败。
Consistency(一致性): 事务开始和结束的数据保持一致的(保持数据的完整性)。
Isolation(隔离性):    事务之间互不影响
Durability(持久性):  数据存储之后,即使系统出现故障,数据也会持久保存。

4. 事务隔离级别

事务的四个隔离级别 isolation level:
read uncommitted 读未提交: A事务可以读B事务未提交的修改数据。问题:【脏读】【不可重复读】【幻读】
read committed   读已提交: A事务可以读取B事务已提交的数据。 【不可重复读】【幻读】
repeatable read 可重复读【默认】: A事务修改数据时,会读取其它事务提交后数据,再进一步修改,保持数据的一致性。 【幻读】							
serializable    序列化或串行化:  多个事务在修改同一条数据时,必须等待其他事务完成才能执行。

客户端设置事务隔离级别的语句:
set session transaction isolation level 隔离级别;

5. 高级子查询

-- join on 方式
	-- 内连接 join on 
	-- 左外连接 left join on  
	-- 右外连接 right join on 
	
	select s.*, sc.*, c.*
	from student s 
	left join score sc on (sc.sn=s.sn)
	right join cource c on (c.cn = sc.cn)
	
-- 等值连接条件(属于内连接)
	select s.*, sc.*, c.*
	from student s, score sc , cource c
	where s.sn  = sc.sn
	and sc.cn = c.cn;

-- 联合查询
	select * from s1
	union all -- all 表示不去重
	select * from s1;

6. MySQL中字符、日期、数值的常用函数

字符函数: lower(), upper(), replace(), instr(), length, char_length(), concat(), lpad(), rpad(), trim()< char(), ord(), format(str, n)  
日期函数: year()/month()/day()/hour()/minute()/week()
         dayofweek() dayofyear() weekofyear()
         date_add(,interval n 日期时间关键字)  date_sub()
         str_to_date() date_format()
             
         now()/current_timestamp
         current_date/curdate()
         current_time/curtime()
数值函数: abs()/round()/ceil()/floor()/pow()/mod()
          sin()/cos()/tan()/con()
          rand()

7. Mysql数据库的增、删、查、改

(1)select语句

select [*|表名.*] [,][ [表名.]字段名 [as 字段别名], [表名.]字段名2 [as 字段名2别名], ...]
from <表名> [as] [表别名]
[join <表名2>  on (表名.列名  = 表名2.列名 )  ]
[where 条件表达式]
[group by 分组的字段]
[having 聚合字段的条件表达式] 
[order by 排列字段]
[limit 起始行号, 每页显示的记录行数]

经典SQL语句:

查询课程编号为01且课程成绩在80分以上的学生的学号和姓名

SELECT st.s_id, st.s_name
FROM student st
JOIN score sc ON sc.s_id=st.s_id WHERE sc.c_id="01" AND sc.s_score>=80

(2) insert语句

-- 新增数据时,可以指定哪些字段及它的数据
-- 默认情况下,为所有的字段指定数据, 字段顺序按创建表时的字段顺序。
insert into 表名 [ ( 字段1, 字段2 ) ]
values (字段1值, 字段2值);

(3) update语句

update 表名 set
字段1=字段1值 [, 字段2=字段2值, ...]
[where 条件表达式]

【注意】如果不增加where条件语句,则表示针对所有的记录(行)的特定的字段修改它的数据。

(4) delete语句

delete from 表名
[where 条件表达式]

【提示】不加where条件的delete语句,表示清空表。

8. 外键约束的级联删除

  • 添加外键约束时, 可以指定级联选项
    • on delete cascade 当主表中的主键所在行的数据被删除时,外键字段所在行则被级联删除
    • on delete set null 当主表中的主键所在行的数据被删除时,外键字段则被设置为null
    • on update cascade 级联更新, 当主键的值发生变化后,外键的值也同步修改。

9. Oder by子句和Limit子句

  • order by用于对查询数据进行排序的,支持升序(默认 ASC)和降序( DESC )两种方式。
-- 查询所有员工的姓名、入职时间和薪资,并按薪资的降序(从大到小)方式排列
-- desc -> descend
-- asc -> ascend
select name,hire_date,salary
from person
order by salary desc;
  • limit子句可以对查询结构的数据进行分页显示,格式:
-- offset 表示起始行的索引编号 ,从0开始
-- rows 表示本次显示的行数,可以理解一页显示的记录数
-- 假如 每一页显示 10记录,第5页的offet是多少?是40
-- 每一页显示 5条记录,第6页的offset是多少?  
-- offset = (page-1)*rows
limit offset, rows

10. mysql查询最新插入的10条数据

select * from table order by id desc limit 10

11. MySQL和Redis的区别

(1)类型上
	从类型上来说,mysql是关系型数据库,redis是非关系型数据库
(2)作用上
	mysql用于持久化的存储数据到硬盘,功能强大,但是速度较慢
	redis用于存储使用较为频繁的数据到内存中,读取速度快
(3)需求上
	mysql和redis因为需求的不同,一般都是配合使用

12.Redis的优缺点

优点

  • 读写性能优异, Redis能读的速度是110000次/s,写的速度是81000次/s。
  • 支持数据持久化,支持AOF和RDB两种持久化方式。
  • 支持事务,Redis的所有操作都是原子性的,同时Redis还支持对几个操作合并后的原子性执行。
  • 数据结构丰富,除了支持string类型的value外还支持hash、set、zset、list等数据结构。
  • 支持主从复制,主机会自动将数据同步到从机,可以进行读写分离。

缺点

  • 数据库容量受到物理内存的限制,不能用作海量数据的高性能读写,因此Redis适合的场景主要局限在较小数据量的高性能操作和运算上。
  • Redis 不具备自动容错和恢复功能,主机从机的宕机都会导致前端部分读写请求失败,需要等待机器重启或者手动切换前端的IP才能恢复。
  • 主机宕机,宕机前有部分数据未能及时同步到从机,切换IP后还会引入数据不一致的问题,降低了系统的可用性。
  • Redis 较难支持在线扩容,在集群容量达到上限时在线扩容会变得很复杂。为避免这一问题,运维人员在系统上线时必须确保有足够的空间,这对资源造成了很大的浪费。

12. Redis常用的数据类型和数据持久化方式

redis是基于k-v的键值对的完全的内存缓存服务,所谓的数据类型是指v的数据类型.
常用的数据类型:
	- str 
	- hash
	- list
	- set  集合
	- zset 有序集合

数据持久化:
	- rdb(Redis DataBase默认)方式,将内存的数据保存到硬盘中, 默认在配置文件使用save项, 根据keys的数量和时间的设置存储条件.
	- aof方式, 将操作的日志进行持久化(追加日志文件).

四、Linux常用命令

1.Linux基本命令

pwd                           显示工作路径                    
tree                          显示文件和目录由根目录开始的树形结构
lstree                        显示文件和目录由根目录开始的树形结构
mkdir dir1                    创建一个叫做 'dir1' 的目录' 
mkdir dir1 dir2               同时创建两个目录 
mkdir -p /tmp/dir1/dir2       创建一个目录树 
rm -f file1                   删除一个叫做 'file1' 的文件' 
rmdir dir1                    删除一个叫做 'dir1' 的目录' 
rm -rf dir1                   删除一个叫做 'dir1' 的目录并同时删除其内容 
rm -rf dir1 dir2              同时删除两个目录及它们的内容 
mv dir1 new_dir               重命名/移动 一个目录 
cp file1 file2                复制一个文件 
cp dir/* .                    复制一个目录下的所有文件到当前工作目录 
cp -a /tmp/dir1 .             复制一个目录到当前工作目录 
cp -a dir1 dir2               复制一个目录 

ln -s file1 lnk1              创建一个指向文件或目录的软链接 
ln file1 lnk1                 创建一个指向文件或目录的物理链接 

2. find文件查找

命令格式: find [path] [表达式语句]

find . -name '*.txt' > txt_files.txt   # 将查找到的所有文本文件结果写入到 txt_files.txt文件中
find code -name "*.py" |xargs cat|grep -E ^$| wc -l   # 统计python的代码行数

3. Cat内容查看

命令格式: cat [OPTION] 文件 -A, --show-all 显示所有内容 -n 显示行号

cat -nb 1.sql  # 显示文件内容,并显示行号, 但空行不显示行号

4. grep命令

当想知道某个关键字xxx在哪些文件里面的哪些行出现的时候,用这个命令。这个命令可以配合正则表达式实现强大的功能

可以和cat或find组合使用,为显示的数据增加显示或查找条件。

cat test2.sql |grep -E ^.+$ | wc -l
grep -E ^.*$ -n test2.sql   # 显示每一行的内容,并显示行号。 -E表示正则

5. xargs命令

是给其他命令传递参数的一个过滤器,常作为组合多个命令的一个工具。它主要用于将标准输入数据转换成命令行参数,xargs能够处理管道或者标准输入并将其转换成特定命令的命令参数。也就是说find的结果经过xargs后,其实将find找出来的文件名逐个作为了grep的参数。grep再在这些文件内容中查找关键字test。

格式: 命令 | xargs [参数] [command ] , 不带command ,默认的使用echo 输出

作用:

- 数据以空格进行分隔
- 可以根据参数进行一次或多次处理,默认的处理命令是/bin/echo
- 空行不进行处理,会被忽略
- 遇到命令状态为255时,xargs会立刻停止,譬如发生错误时.
find code -name '*.py'|xargs python             #查到 code目录下所有python脚本,并执行此脚本
# find、grep、xargs结合使用:
find . -name "aa*" | xargs grep hello  -ls  # 则输出的是文件内容 包含“hello”的aa文件

6. ps进程查看

参数说明:

-A :所有的进程均显示出来,与 -e 具有同样的效用;
-a : 显示现行终端机下的所有进程,包括其他用户的进程;
-u :以用户为主的进程状态 ;
x :通常与 a 这个参数一起使用,可列出较完整信息。
l :较长、较详细的将该PID 的的信息列出;
j :工作的格式 (jobs format)
-f :做一个更为完整的输出。
ps -l
ps -ef    # 显示进程完整的信息

查看所有的sshd远程的进程

ps -ef|grep sshd | grep -v grep

7. netstat端口占用查看命令

netstat -nltp | grep 22     #查看 22端口的占用情况

8. free查看内存命令

#查看内存使用情况:
free -mh
可以使用 top命令,查看CPU和内存使用情况。

9. df查看磁盘空间命令

参数:

-a, --all               所有文件信息: pseudo 虚假的, duplicate 重复的, inaccessible 不可达
-h, --human-readable    显示人性化的文件大小 (如, 1K 234M 2G)
-i, --inodes		    显示inode 信息而非块使用量
-k			            即--block-size=1K
-l, --local		        只显示本机的文件系统
-T, --print-type        显示文件类型
df -h    #显示存储空间大小

10. du查看目录命令

du -sh

-s 统计求和,将所有目录及文件的大小的总和计算出来。结果只有一行。

11. firewall-cmd命令

查看状态
firewall-cmd --state
开启与关闭
service firewalld start|stop|restart
# systemctl start|stop|restart firewalld
启用与禁用
systemctl disable|enable firewalld
开放端口
firewall-cmd --add-port=80/tcp --permanent

开放端口后,需要重载配置,命令如下:

firewall-cmd --reload

12. Chmod、Chown命令

r:4  w:2  x:1
chmod  [ugoa][+-=][rwx]  filename   # 修改权限
chmod +x aa.sh     # 给aa.sh文件添加可执行权限

chown   # 修改文件所属组及用户
格式: chown [-fhnv] [-R [-H | -L | -P]] owner[:group] file 

13. vim

通过vim编辑一些文本或脚本文件。

三种模式

​ 普通模式、插入模式、命令行模式

1、普通模式命令:

[n]yy: 复制, 当前光标位置向下复制n行,n默认为1行

[n]dd: 剪贴,当前光标位置向下剪贴n行,n默认为1行

d: 按左右方向键 删除光标左或右边的一个字符, 按上下方向键,删除当前行和上行或下行的两行内容。

nd: 向上或下删除 n+1 行, 向左或右删除 n个字符

p: 粘贴

​ **x:**光标所在位置删除一个字符

nx: 光标所在位置开始向右删除n个字符。如果删除最一个位置,光标自动向左移动。

​ **dw:**光标位置开始到行尾全部删除, 遇到空格或特殊字符结束。

gg: 第一行

​ **G:**光标移动到最后一行首位置

​ dG(Shift+g): 从当前行删除到最后一行

ggdG: 清空文件

​ v: 进入可视模式,移动光标选择字符

shift+v: 选择整行

. : 重复操作

u: 撤销操作

ctrl+r: 取消撤消

/ : 查找字符

2、插入模式

i:光标当前位置 insert

Shift + i: 光标所在行首 insert

a:光标所在的后一个位置 insert

Shift + a: 光标所在行尾 insert

o: 光标所在行下插入空行

Shift + o: 光标所在行上插入空行

cc: 剪切当前行,进入当前。 和dd命令相似,但dd不会进入编辑模式

r:只替换一个,之后变成 普通模式

Shift + r ®:替换模式,可连续替换,直到模式切换为止。

3、命令行模式

  1. set number 显示行号
  2. set nonumber 取消显示行号
  3. n 移动第n行
  4. /内容 搜索内容
  5. %s/原内容/新内容/ig 全文或当前行替换内容
  6. set ft=UNIX 设置文件格式为UNIX格式, 解决Window上开发的sh脚本不能在Linux中执行的问题。
  7. w 写入
  8. q 退出, q! 强制退出
  9. x 写入并退出

14. sed

sed是一种流编辑器,它是文本处理中非常好的工具,能够完美的配合正则表达式使用,功能不同凡响。处理时,把当前处理的行存储在临时缓冲区中,称为“模式空间”(pattern space),接着用sed命令处理缓冲区中的内容,处理完成后,把缓冲区的内容送往屏幕。接着处理下一行,这样不断重复,直到文件末尾。文件内容并没有改变,除非你使用重定向存储输出。Sed主要用来自动编辑一个或多个文件,可以将数据行进行替换、删除、新增、选取等特定工作,简化对文件的反复操作,编写转换程序等。

# sed [-nefr] [动作]

选项与参数:
-n :使用安静(silent)模式。在一般 sed 的用法中,所有来自 STDIN 的数据一般都会被列出到终端上。但如果加上 -n 参数后,则只有经过sed 特殊处理的那一行(或者动作)才会被列出来。
-e :直接在命令列模式上进行 sed 的动作编辑;
-f :直接将 sed 的动作写在一个文件内, -f filename 则可以运行 filename 内的 sed 动作;
-r :sed 的动作支持的是延伸型正规表示法的语法。(默认是基础正规表示法语法)
-i :直接修改读取的文件内容,而不是输出到终端。

动作说明: [n1[,n2]]function
n1, n2 :不见得会存在,一般代表『选择进行动作的行数』,举例来说,如果我的动作是需要在 10 到 20 行之间进行的,则『 10,20[动作行为] 』

function:
a :新增, a 的后面可以接字串,而这些字串会在新的一行出现(目前的下一行)~
c :取代, c 的后面可以接字串,这些字串可以取代 n1,n2 之间的行!
d :删除,因为是删除啊,所以 d 后面通常不接任何咚咚;
i :插入, i 的后面可以接字串,而这些字串会在新的一行出现(目前的上一行);
p :列印,亦即将某个选择的数据印出。通常 p 会与参数 sed -n 一起运行~
s :取代,可以直接进行取代的工作哩!通常这个 s 的动作可以搭配正规表示法!例如 1,20s/old/new/g 就是啦!

替换字符串:

sed -i "s/要被取代的字串/新的字串/g" `grep 查找字段 -rl 路径`

15. Shell

shell 中while循环

while (( condition ))
do
	command
done

# 例:
i=1
s=0
while (( $i<=100 ))
do
    s=$[i+s]
    echo "$i, $s"
    let "i++"                # 执行 字符串的shell表达式,等价于 i=$[i+1]
done

shell 中读取文件第一行

head -n +1 file.txt

shell 中读取文件最后一行

tail -n -1 file.txt

五、Docker工具

Docker是镜像容器的管理工具, 实现虚拟化技术 替代类传于VMware或VirtualBox虑拟环境。Docker基于硬件虚拟化, 实现多个独立的容器(微型操作系统)在同等的硬件资源下和基础的操作系统(Ubuntu)上构建的虚拟环境。

1. Docker常用命令

docker version                      检查docker环境
systemctl start docker              启动docker后台服务
systemctl enable docker             开机启动docker服务
systemctl disable docker            取消开机启动docker服务
docker pull 镜像名:版本号             下拉镜像
docker images                       查看本地镜像
docker ps [-a|l]                    查看容器的运行状态
docker rmi [-f] 镜像全名[:tag]或ID    删除镜像
docker exec -it 容器名或ID bash       进入容器
运行镜像
docker run [-i -t -d] [--name name] [-v local_path:path] [-p local_port:port] [--network network_name] [-e envirment_name=value] 镜像全名[:tag]或ID
  • -i 表示input,可以进入容器中,

  • -t 表示打开新的terminal 终端, 一般和-i组合使用

  • -d 让容器处于后台运行, 同步返回容器的ID

  • –name 指定容器的名称

  • -v (Volumn) 将本地的文件路径和容器的文件绑定,同步文件数据。

  • -p 将本地的端口与容器中服务端口绑定。

  • -e 指定容器的环境变量,容器的程序在运行时需要的变量。

2. Docker复制文件

通过docker cp命令将本地文件复制到容器中,或者容器中文件复制到本地来。

docker cp 容器名:源文件路径  本地目标路径
docker cp 本地源路径 容器名:目标文件路径

3. 容器日志

当容器启动后,可以查看容器中服务的运行日志:

docker logs 容器名或ID

4. Docker镜像和容器的关系

  • 容器由镜像运行产生的(运行镜像产生容器)
  • 一个镜像可以运行多次,但每一次产生的容器名称和ID是唯一的。
  • 容器可以保存为镜像(docker save)

六、框架相关知识

1. Django中的MVC、MTV和MVVM

MVC把Web应用分为模型(Model),控制器(Controller)和视图(View)三层,他们之间以一种插件式的、松耦合的方式连接在一起,模型负责业务对象与数据库的映射(ORM),视图负责与用户的交互(页面),控制器接受用户的输入调用模型和视图完成用户的请求

MTV模式本质上和MVC是一样的。
	M 代表模型(Model):负责业务对象和数据库的关系映射(ORM)。
	T 代表模板 (Template):负责如何把页面展示给用户(html)。
	V 代表视图(View):负责业务逻辑,并在适当时候调用Model和Template。 

MVVM:也是MVC的, M和VM分别表示模型和视图模型(控制器/监视器), V单纯的视图(用户交互的UI页面)。其中VM是MVC中的控制器,主要监听V(视图)的事件和M(数据模型)的变化,并即时渲染到V中。MVVM主要应用于Vue框架。

2. FBV和CBV

FBV: Function based View 基于函数的视图。Djang和Flask都是默认支持的。
CBV: Class based View 基于类的视图。如Django的View或DRF的APIView类。

FBV和CBV的区别:
 - FBV 处理业务都集中一个函数中,无论是哪一个请求方法(GET/POST)。一般与MVT组合使用。因为HTML页面的Form表单标签只支持GET和POST请求。
 - CBV 处理的业务分散不同的实例方法中,如get(), post()。开发RESTful接口时,显得更规范一些。主要面向的资源的业务开发(API接口)。
2.1 CBV加装饰器

需要先导入模块:

from django.utils.decorators import method_decorator
  • 装饰类时,语法为:@method_decorator(装饰器函数名, name=‘类内需要装饰的方法名’)

  • 直接装饰类内的方法时,直接**@method_decorator(装饰器函数名)**即可。我们这里不用原生的装饰器是因为扩展性差,因为类方法有一个self参数,如果用原生的装饰器就需要加一个参数,可是加了之后装饰器又没法装饰普通的函数了。

    # @method_decorator(login_auth,name='get')  # 第二种 name参数必须指定
    class MyHome(View):
        @method_decorator(login_auth)  # 第三种  get和post都会被装饰
        def dispatch(self, request, *args, **kwargs):
            super().dispatch(request,*args,**kwargs)
            
        # @method_decorator(login_auth)  # 第一种
        def get(self,request):
            return HttpResponse('get')
    
        def post(self,request):
            return HttpResponse('post')
    

FBV,csrf_token认证情况

情况一:所有函数都要认证,某一个不需要认证 (@csrf_exempt)

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware', # 全站使用csrf认证
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]	

from django.views.decorators.csrf import csrf_exempt

@csrf_exempt # 该函数无需认证
def users(request):
    user_list = ['tom','jeck']
    return HttpResponse(json.dumps((user_list)))

情况二:所有都不认证,某个函数需要认证(@csrf_protect)

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    #'django.middleware.csrf.CsrfViewMiddleware', # 全站不使用csrf认证
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]


from django.views.decorators.csrf import csrf_exempt

@csrf_protect # 该函数需认证
def users(request):
    user_list = ['tom','jeck']
    return HttpResponse(json.dumps((user_list)))

CBV,csrf_token认证时需要使用

  • @method_decorator(csrf_exempt)
  • 在dispatch方法中(单独方法无效)
# 方式一:
from django.views.decorators.csrf import csrf_exempt,csrf_protect
from django.utils.decorators import method_decorator

class StudentsView(View):

	@method_decorator(csrf_exempt)
    def dispatch(self, request, *args, **kwargs):
    	return super(StudentsView,self).dispatch(request, *args, **kwargs)

    def get(self,request,*args,**kwargs):
    	print('get方法')
    	return HttpResponse('GET')

    def post(self, request, *args, **kwargs):
    	return HttpResponse('POST')

    def put(self, request, *args, **kwargs):
    	return HttpResponse('PUT')

    def delete(self, request, *args, **kwargs):
    	return HttpResponse('DELETE')
    
# 方式二:
from django.views.decorators.csrf import csrf_exempt,csrf_protect
from django.utils.decorators import method_decorator

@method_decorator(csrf_exempt,name='dispatch')
class StudentsView(View):

    def get(self,request,*args,**kwargs):
        print('get方法')
        return HttpResponse('GET')

    def post(self, request, *args, **kwargs):
    	return HttpResponse('POST')

    def put(self, request, *args, **kwargs):
    	return HttpResponse('PUT')

    def delete(self, request, *args, **kwargs):
    	return HttpResponse('DELETE')

3. RESTful设计的规范

RESTful主要是面向资源设计它的接口的, 它有四个规范:
 - 每个资源都存在唯一的标识URI
 - 每个资源都具有四个动作谓词, 分别是GET/POST/PUT/DELETE
 - 每次的动作都是无状态的, 即是HTTP的短连接(Connection: close|keep-alive)
 - 交互的资源数据类型一般是json或xml.

4. Django、Flask、Tornado三个框架的区别

从体积方面: 
	- Django重力级的,组件或功能更全面,不需要过多地引用第三方插件(组件).内部包含MVC/MTV/ORM/Cache/Logger/Admin站点/权限/Session等.
	- Flask 微框架, 只实现Web服务的核心引擎,基于Werkzeug实现WSGI服务.如果操作数据库则需要自定义或引用第三方的SQLAlchemy.
	- Tornado 除了现实现WSGI服务之外,包含异步请求模块. 整体被设计为高并发/高性能的异步服务框架.
从性能方面:
	- Django 的json序列化最优的,  ORM操作比较慢的. 在Tornado和Flash中使用SQLAlchemy性能较高.
	- Tornado的响应的速度最快的,大概在1秒; Django和Flask大概在3.4秒左右.
	- Flask的单独使用其它组件时,处理速度是最快的. 在实现少量接口的业务中,开发速度是最快的.
		- NoSQL(Redis/MongoDB/Memchache/ElatichSearch/Hive/Hbase)

5. Django中间件的钩子函数

常用四个钩子函数:
- process_request(self, request, *args, **kwargs)
  处理请求前,在每个请求上,request对象产生之后,url匹配之前调用,返回None或HttpResponse
  
- process_view(self, request, view, *args, **kwargs)
  处理视图前,在每个请求上,url匹配之后,视图函数调用之前调用,返回None或HttpResponse
  
- process_exception(self, request, exception)
  异常处理,当视图抛出异常时调用
  
- process_response(self, request, response)
  处理响应后,视图函数调用之后,所有响应返回浏览器之前被调用,在每个请求被调用,返回HttpResponse对象
- process_template_response(self, request, response):

6. ORM及实现原理

ORM: ORM是对象关系映射(Object Relational Mapping)的缩写,Django的ORM操作本质上会根据对接的数据库引擎,翻译成对应的sql语句,用面向对象的方式去操作数据库的创建表、增加、修改、删除、查询等操作。

实现原理: 在Python中可以通过元类、类和对象的自省函数来实现表结构的定义、实例对象的CURD等功能。
现有的ORM: Django ORM 和 SQLAlchemy
6.1 orm中的方法
<1> all(): 查询所有结果
<2> filter(**kwargs): 它包含了与所给筛选条件相匹配的对象
<3> get(**kwargs): 返回与所给筛选条件相匹配的对象,返回结果有且只有一个,如果符合筛选条件的对象超过一个或者没有都会抛出错误。
<4> exclude(**kwargs): 它包含了与所给筛选条件不匹配的对象
<5> order_by(*field): 对查询结果排序
<6> reverse(): 对查询结果反向排序
<8> count(): 返回数据库中匹配查询(QuerySet)的对象数量。
<9> first(): 返回第一条记录
<10> last(): 返回最后一条记录
<11> exists(): 如果QuerySet包含数据,就返回True,否则返回False
<12> values(*field): 返回一个ValueQuerySet——一个特殊的QuerySet,运行后得到的并不是一系列model的实例化对象,而是一个可迭	代的字典序列
<13> values_list(*field): 它与values()非常相似,它返回的是一个元组序列,values返回的是一个字典序列
<14> distinct(): 从返回结果中剔除重复纪录
<15> delete(): 立即删除对象而不返回任何值,delete() 方法是 QuerySet 上的方法,但并不适用于 Manager 本身。
<16> update(): 对于任何结果集(QuerySet)均有效,这意味着可以同时更新多条记录。
	 update()方法会返回一个整型数值,表示受影响的记录条数。
6.2 django orm中三种能写sql语句的方法

<1> extra: 结果集修改器,一种提供额外查询参数的机制

# 查找小玖出版社中所有大于100元的书籍
Book.objects.filter(publisher__name = '小玖出版社').extra(where = ['price > 100']) 

# 或
Book.objects.filter(publisher__name = '小玖出版社', price__gt = 100)

<2> raw: 执行原始sql并返回模型实例

# 查看数据表python_book 中的所有的对象。
b = Book.objects.raw('select * from python_book') # raw方法返回的是RawQuerySet的对象,可迭代
for i in b:
	print(i.title)

<3>直接执行自定义SQL(这种方法完全不依赖与model,前面两种方式还是要依赖于model),其实就是用python操作数据库的方法

# 获取数据库的连接对象
from django.db import connection
# 获得一个游标cursor对象
cursor = connection.cursor()
# 插入操作
cursor.execute("insert into python_author(name) values('小玖')")
# 更新操作
cursor.execute("update python_author set name = '小柒' where name = '小玖' ")
# 删除操作
cursor.execute("delete from python_author where name = '小柒'")
# 查询操作
cursor.execute('select * from python_author')
# 一条一条的取数据
raw = cursor.fetchone()
# 取出所有数据,以元组的形式返
cursor.fetchall()

7. DRF可用的权限验证类

rest_framework.permissions.AllowAny
rest_framework.permissions.IsAuthenticated
rest_framework.permissions.IsAuthenticatedOrAnonReadOnly
rest_framework.permissions.IsAdminUser
rest_framework.permissions.DjangoModelPermissions
rest_framework.permissions.DjangoModelPermissionsOrAnonReadOnly

8. Django请求生命周期

(1)用户输入网址,浏览器发起请求
(2)WSGI(服务器网关接口)创建socket服务端,接受请求
(3)中间件处理请求
(4)url路由,根据当前请求的url找到相应的视图函数
(5)进入view,进行业务处理,执行类或者函数,返回字符串
(6)再次通过中间件处理响应
(7)WSGI返回响应
(8)浏览器渲染

9. Nginx负载均衡策略

(1)RR (轮询策略)
按照轮询(默认)方式进行负载,每个请求按时间顺序逐一分配到不同的后端服务器,如果后端服务器down掉,能自动剔除。虽然这种方式简便、成本低廉。但缺点是:可靠性低和负载分配不均衡。

(2)权重weight
指定轮询几率,weight和访问比率成正比,用于后端服务器性能不均的情况。
upstream test{
     server localhost:8080 weight=9;
     server localhost:8081 weight=1;
}

(3)ip_hash 
 上面的2种方式都有一个问题,那就是下一个请求来的时候请求可能分发到另外一个服务器,当我们的程序不是无状态的时候(采用了session保存数据),这时候就有一个很大的很问题了,比如把登录信息保存到了session中,那么跳转到另外一台服务器的时候就需要重新登录了,所以很多时候我们需要一个客户只访问一个服务器,那么就需要用iphash了,iphash的每个请求按访问ip的hash结果分配,这样每个访客固定访问一个后端服务器,可以解决session的问题。
 upstream test {
    ip_hash;
    server localhost:8080;
    server localhost:8081;
}

(4)fair(第三方)
按后端服务器的响应时间来分配请求,响应时间短的优先分配。
upstream backend { 
    fair; 
    server localhost:8080;
    server localhost:8081;
}

10. celery

Celery是一个简单、灵活且可靠的,处理大量消息的分布式系统,专注于实时处理的异步任务队列,同时也支持任务调度。

分布式系统:

​ 一个系统应用(网站系统应用)会涉及到很多的组件(web服务器、web应用、数据库、消息中间件等),将一个系统应用的相关组件架构在不同的服务器上,不同服务器上不同组件之间进行消息通信的这种模式,就叫做分布式系统。可以实现负载均衡。

10.1 celery的优点:
  • 简单**

    Celery 易于使用和维护,并且它 不需要配置文件

    下面是一个你可以实现的最简应用:

    from celery import Celery
    
    app = Celery('hello', broker='amqp://guest@localhost//')
    
    @app.task
    def hello():
        return 'hello world'
    
  • 高可用性

    woker和client会在网络连接丢失或者失败时,自动进行重试。并且有的brokers(消息中间件) 也支持“双主”或者“主/从”的方式实现高可用
    
  • 快速

    单个 Celery 进程每分钟可处理数以百万计的任务,而保持往返延迟在亚毫秒级(使用 RabbitMQ、py-librabbitmq 和优化过的设置)。

  • 灵活

    Celery 几乎所有部分都可以扩展或单独使用。可以自制连接池、 序列化、压缩模式、日志、调度器、消费者、生产者、自动扩展、 中间人传输或更多。

10.2 使用场景:

充值成功,发送短信通知耗时操作

七、爬虫相关知识

1. 爬虫的常见反爬虫策略

- UA:User-Agent
- Cookie
- 频次(请求的次数):IP代理
- 验证码(短信、图片:字母和数字、勾选、滑块)
- 动态JS(Selenium/Splash)
- Referer 引用来源         

2. requests.request()方法中常用参数

requests.request(method, url, params,data,json, **kwargs)
    - method
    - url
    - params
    - data
    - json
    - files
    - headers
    - cookies
    - auth
    - proxies

基于request()方法实现的快捷方法:
    requests.get(url, params, **kwargs)
    requests.post(url, data, json, **kwargs)
    requests.put(url, data,json, **kwargs)
    requests.delete(url, params, **kwargs)

3. scrapy框架的常用命令

scrapy startproject 项目名
scrapy genspider 爬虫名 域名
scrapy crawl 爬虫名 [-o 文件名]
scrapy shell [url]

4. scrapy的核心组件

engine 引擎, 负责其他四个组件的交互.
spider 爬虫类, 启动爬虫的入口(发起起始的请求)/请求成功之后的数据解析/解析过程的产出item和request
scheduler 调度器, 接收engine传入的请求,根据调度规则(考虑优先级), 产出一个request给engine.
downloader 下载器, 接收engien从调度器获取的请求request, 并开始下载,下载成功之后,产出给engine
itempipeline 数据管道, 处理engien从爬虫类中接收到解析数据item, 并进行处理.
两个中间件:   爬虫中间件, 下载中间件

在这里插入图片描述

  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值