度的最大值为2
,最下一层有右子树就必须有左子树,叶子节点只能出现在倒数2层或者1层
出叶子节点外,度为2
,左子树的所有节点都比根节点小,右子树的所有节点都比根节点大
,左子树和右子树的高度差不大于1
排序算法:冒泡排序;快速排序,直接插入排序;希尔排序,选择排序,二路归并排序,基数排序;计数排序
搜索算法:顺序搜索,折半查找,差值查找,跳跃查找
斐波拉茄数列
# 冒泡排序:两层循环,逐个比较
def bubble_sort(seq):
for i in range(len(seq) - 1):
for j in range(i + 1, len(seq)):
if seq[i] > seq[j]:
seq[i], seq[j] = seq[j], seq[i]
return seq
# 快速排序:找到基准值,大的放右边,小的放左面
def quick_sort(arr):
if len(arr) <= 1:
return arr
pivot = arr[len(arr) // 2]
left = [x for x in arr if x < pivot]
middle = [x for x in arr if x == pivot]
right = [x for x in arr if x > pivot]
return quick_sort(left) + middle + quick_sort(right)
# 插入排序:第一个值有序,让待排元素与有序数列从后向前比较,直到有序
def insert_sort(seq):
for index in range(1, len(seq)):
while (index - 1 >= 0 and seq[index - 1] > seq[index]):
seq[index], seq[index - 1] = seq[index - 1], seq[index]
index = index - 1
return seq
# 希尔排序:缩小增量排序,插入排序是希尔排序的特殊情况。
def shell_sort(seq):
gap = len(seq) // 2
while (gap > 0):
for i in range(gap, len(seq)):
while (i - gap >= 0 and seq[i - gap] > seq[i]):
seq[i - gap], seq[i] = seq[i], seq[i - gap]
i -= gap
gap //= 2
return seq
# 二路归并排序 分分合合
def merge_sort(seq):
if len(seq) < 2:
return seq
mid = len(seq) // 2
left, right = seq[0:mid], seq[mid:]
return merge(merge_sort(left), merge_sort(right))
def merge(left, right):
result = []
while left and right:
result.append(left.pop(0) if left[0] < right[0] else right.pop(0))
if left:
result.extend(left)
if right:
result.extend(right)
return result
# 选择排序,每次选择一个最小值进行排序
def select_sort(seq):
for i in range(len(seq) - 1):
minIndex = i
for j in range(i + 1, len(seq)):
if seq[minIndex] > seq[j]:
minIndex = j
seq[i], seq[minIndex] = seq[minIndex], seq[i]
return seq
# 基数排序:先按照某个元素排序,在按照次要元素排序,最终有序, 基于关键字的排序
def radix_sort(seq):
max_length = len(str(max(seq))) # 最大长度
for i in range(max_length):
ret = [[] for i in range(10)]
# 在按照第二位进行排序的时候需要在生成新的桶
for value in seq:
ret[(value//(10**i))%10].append(value)
seq = [i for item in ret for i in item]
return seq
# 计数排序:不进行比较,让他根据空列表下标回到有序状态
def count_sort(seq:list):
'''
最简单的一种,适用于最大值比较小,并且分布在一个有限的额区间
'''
max_value = max(seq)+1
count = [0]*max_value
for value in seq:
count[value] += 1
# 得到了count数组,然后将数组元素还原
ret = []
# for value,c in enumerate(count):
# if value:
# for _ in range(c):
# ret.append(value)
for i in range(len(count)):
while count[i] > 0:
ret.append(i)
count[i] -= 1
return ret
这种计数排序无法保证稳定性
# 稳定的计数排序:通过累加数组确定某个元素的下标值
def count_sort_pro(seq:list):
max_value = max(seq)+1
count = [0]*max_value
for value in seq:
count[value] += 1
# 得到了count数组,然后将数组元素还原
# 把得到的数组进行;累加,目的是找到位数
for i in range(1, len(count)):
count[i] += count[i-1]
# 现在的数组为累加数组
# 假设给的初始数组为[1,2,2,2,2,4,4,4]
# 得到的计数数组为[0,1,4,0,3]
# 得到的累加数组为[0,1,5,5,8]
# 反向填充数组
ret = [0 for i in range(len(seq))]
for i in range(len(seq)-1,-1,-1):
ret[count[seq[i]]-1] = seq[i]
count[seq[i]] -= 1
return ret
# 顺序搜索不说了
# 折半查找:又叫做二分查找,需要有序。
def binary_search(sorted_seq, target):
left = 0
right = len(sorted_seq) - 1
while (left <= right):
mid = (left + right) // 2
if sorted_seq[mid] == target:
return mid
if sorted_seq[mid] > target:
right = mid - 1
if sorted_seq[mid] < target:
left = mid + 1
return None
# 折半查找太傻了,每次都二分,其实可以根据target值的大小大体估计元素所在位置
# 就像在字典中找dear,你会靠前翻,而不是从中间去确认。
def insert_search(sorted_seq, target):
left = 0
right = len(sorted_seq) - 1
if target < sorted_seq[left] or target > sorted_seq[right]:
return None
while (left <= right):
if (sorted_seq[right] == sorted_seq[left]):
return None
mid = left + ((right - left) * (target -
sorted_seq[left])) // (sorted_seq[right] - sorted_seq[left])
if sorted_seq[mid] == target:
return mid
if sorted_seq[mid] > target:
right = mid - 1
if sorted_seq[mid] < target:
left = mid + 1
return None
# 把它想象成一个均匀增加的数列,于是根据相似定理有:
# (target-seq[left])/mid-left <=>(seq[right]-seq[left])/right-left
# 从零开始跳跃固定步数,当某值大于target时,target的返回就锁定在当前下标的前面,
# 当前下标减去步数的后面,没有就说明没有找到target
def jump_search(sorted_seq, target):
if target > len(sorted_seq):
return None
step = int(math.floor(math.sqrt(len(sorted_seq))))
base = 0
while sorted_seq[base] < target:
base += step
for i in range(base - step, base + 1):
if sorted_seq[i] == target:
return i
# 基于yield实现
def fab(n):
a, b, count = 0,1,0
while count < n:
yield b
a, b = b, a+b
count += 1
osi:应用层,表示层,会话层,传输层,网络层,数据链路层,物理层
tcp/ip:应用层,传输层,网络层,网络接口层
5层:应用层,传输层,网络层,数据链路层,物理层
HTTPS就是http的安全版,增加了tls来保证数据的安全性
客户端发送一条信息其中包含可供选择的加密算法,压缩算法,以及版本号
服务器向客户端发送选择的加密算法,压缩算法,版本号,CA证书(其中包含公钥)
客户端判断是否选择证书,若相信,生成一串伪随机数,通过公钥进行加密发送给服务器端
服务器端使用私钥就行解密,然后根据这串随机数得到对称加密秘钥
协商好对称加密秘钥之后,接下来的内容使用该秘钥进行加密传输
TCP提供了一种面向连接的,可靠的字节流服务
怎么保证可靠:通过确认重传,校验和,合理的分片和排序,滑动窗口实现流量控制,动态的改变窗口的大小来实现拥塞控制。
三次握手:首先客户端向服务器端发送SYN同步信号和序列号seqx,服务器收到后发送ack=1,ACKnumber=seqx+1,seqy,SYN,此时服务器处于半链接状态,然后客户端收到信息,发送确认信号与服务器进行连接
SYN攻击:SYN攻击指的是攻击者构造大量不存在的IP地址向服务器发送请求连接,服务器收到信号后向ip地址发送确认信号,由于ip地址不存在,导致服务器会一直重传,占用未连接队列,正确的SYN包被丢弃,让系统运行缓慢。
SYN攻击的解决方案:增加未连接队列的长度,缩短超时时间,syncookie技术(大体理解就是在发送完syn和ack后先不分配资源,计算出cookie值,这个cookie值作为synack的初始序列,然后得到客户端的ack后验证cookie在进行连接)
四次挥手:客户端传输完数据发送FIN,服务器端接收后返回ack,继续发送数据,发送完数据向客户端发送FIN,客户端接收信息后发送ack并进入等待状态time_wait
,服务器端接收后断开连接。客户端等待的原因是:1。保证自己的ack确认信号被服务器端接收了,若服务器会超时重传,而客户端因断开连接接收不到数据造成服务器资源的浪费。2。2MSL是报文在网络上的最大存活时间,保证客户端断开连接后不会有报文去干扰服务器。
Keepalive当很长时间内客户端没有发送数据,服务器向它发送一个空的数据,如果收到回应,表示客户端没有关机,如果多次无回应,说明客户端可能异常关机了,应该断开连接。
滑动窗口 - 拥塞窗口:
同属于传输层协议,缺乏可靠性不能保证数据发送到接受党,也不能保证发送的数据是否有序,也没有确认重传的机制,面向无连接,报文有固定的长度。
网络层协议,提供点对点的通信。
24种设计模式
创建型:工程方法、抽象工厂,原型、单例、建造者
结构型:适配器、桥接、组合、外观、装饰器、享元、代理
行为型:解释器、模板方法、责任链、命令、迭代、中介者、备忘录、观察者、状态、策略、访问者
实现:<<<< 转载 >>>>
6大原则
全部大写,加上HTTP_
,对于自定义的字段,直接大写,加上前缀HTTP_
什么是restful规范?
1 尽量使用https协议
2 对于api接口尽量放在专有的域名下,或者通过url体现
3 版本,可以方便的进行版本的迭代
4 网络上的所有东西都是资源,所以网址中不能有动词,若是一个集合,应该使用复数形式
5 对于资源的具体操作通过HTTP动词实现,一般有get,put,post,patch,delete等
6 对于资源数目较多时,应该使用限制条件进行过滤,适当的分页。
7 状态码,200,300,403,404,500等
8 错误处理,要有对错误信息的描述
9 返回结果,针对不同的http动词应该有不同的返回结果
10 Hyperlink api,对于列表中的信息,每个对象都是可访问的。
DRF的源码流程?
从url开始,找到对应的视图类,进入视图类执行as_view方法,这个方法进行了重写,返回了一个免除csrf验证的view函数,这个函数没有重写,所以要从父类View中找,这个方法返回self.dispatch(),然后进入dispatch()方法, 在dispatch方法中显示对request对象进行封装,封装包括之前的request对象,多了parsers和authentications,这两个对象都是通过遍历类中的classes列表,然后实例化对象,所以现在parsers就是一个装有解析器类实例的列表,authentications也是一样,因为在访问时多次用到,所以放在一个变量中,无需多次实例化。然后在回到dispatch函数中,到下一部,将该类进行初始化,初始化的时候version, scheme = self.determine_version(),该该方法调用的是scheme的determine_version方法,而scheme就是版本类的实例,所以版本类应该返回一个元祖,一个是版本号,另一个是版本类。版本进行完就到了权限认证节流 ,performe_authentication, check_permission, check_throttle, 认证返回的是request.user, 其他两个都是遍历classes列表然后实例化对象,并执行核心函数。request.user是一个方法,他会通过反射机制看request中有没有_user,如果没有,就执行_authenticate方法,该方法和check_permission方法差不多,遍历认证实例,然后执行对应的核心函数。当使用request.data时,这个方法和request.user是一样的,首先通过反射的方式看_data,存在返回,不存在就执行load_data_and_file方法,这个方法会去找到parsers,找到对应的解析方式,然后调 用核心方法进行解析。
如何解决跨域问题?
一. JsonP:ajax支持jsonp格式,
type:jsonp
, 后端有支持jsonp的解析器就可以使用
二. 直接修改响应 :
response = HttpResponse(json.dumps({"key": "value", "key2": "value"}))
response["Access-Control-Allow-Origin"] = "*"
response["Access-Control-Allow-Methods"] = "POST, GET, OPTIONS"
response["Access-Control-Max-Age"] = "1000"
response["Access-Control-Allow-Headers"] = "*"
return response
三. 安装django-cors-headers模块,然后进行简单的配置
# 安装该模块,并将该模块注册到app中,然后在中间件最前面加上
'corsheaders.middleware.CorsMiddleware',
# 配置:https://github.com/adamchainz/django-cors-headers
四. 通过Nginx代理,在Nginx配置中,将发送的请求设置路由匹配,交给某个模块进行处理
例如访问的是一个图片,而访问图片端口是8888,可以经过nginx,发送给fdfs_module模块处理
或者代理到真正的服务器地址。
authentication_classes = [ My_authentication, ],
未认证用户的设置{ 'UNAUTHENTICATED_USER':None}
,未认证的token设置{ 'UNAUTHENTICATED_TOKEN':None,}
REST_FRAMEWORK= {'DEFAULF_AUTHENTICATION_CLASSES':'My_authentication'}
permission_classes = [My_permission,]
{'DEFAULT_PERMISSION_CLASSES':'My_permission'}
return request.META.get('REMOTE_ADDR')
1.2 若继承自Base_Throttle,需要重写allow_request()方法,返回True或者False 2. 设置访问频率可以通过再类中设置rate=‘3/m’3次每分
,或者通过设置scope= ‘throttle_name’,然后在全局中设置scope的频率, 'DEFAULT_THROTTLE_RATES':{ 'throttle_name':'3/m', }
throttle_classes = [My_Throttle,]
{'DEFAULT_Thtottle_CLASSES':'My_throttle'}
'DEFAULT_VERSIONING_CLASS':'rest_framework.versioning.URLPathVersioning',
url or re_path(r'^(?P[v1|v2]+)/order/$', OrderView.as_view())
# 类的继承
class Serializer(serializers.Serializer):
# 字段的定义: 与模型类相同,或者指定source,或者自定义。
username = serializers.CharFiled()
pwd = serializers.CharFiled(source = 'password') #
user_type = serializers.CharField(source = 'get_user_type_display') # Choice字段获取值
group = serializer.Charfield(source = 'group.title') # 外键
roles = serializers.SerializerMethodField() # 同时要定义函数get_字段
def get_roles(self,userinfo):# 传入的值是当前的模型类的实例
roles_obj = userinfo.roles.all()
ret = []
for item in roles_obj:
ret.append(
{'id':item.id, 'title':item.title }
)
return ret
# 特殊字段,view_name是详情页的视图namespace:app_name
# pk为默认值,路由中使用pk即可
# id通常为主键
group = serializers.HyperlinkedIdentityField(view_name='app:gp',lookup_url_kwarg='pk',lookup_field='id')
# 如果是外键属性则使用lookup_field='groud_id' ####
class Serializer(serializers.ModelSerializer):
class Meta:
model = UserInfo # 根据这个模型类,自动生成它的字段
# fields = '__all__'
fields = ['id','username', 'password', 'usertype', 'roles', 'group']
depth = 1 # 深度为1
序列化的使用
# 当有HyperLink字段时需要传入context字段,结果集many=True
ser = UserSerializer(instance=user,many=False,context={'request':request})
return Response(ser.data)
反序列化
反序列化
首先将前台数据reques.data
传过来,得到一个序列化的实例
判断数据是否合法,在序列化类中执行校验(局部钩子和全局钩子)
通过校验:保存数据,执行序列化对象的save()方法,前提是实现序列化类的create()方法。
不通过:返回序列化对象的错误信息
在创建序列化对象的时候,在Fied中(required = True,max_length = 12 ,
# error_messages={'required':'这个字段必须','max_length':'太长了'})
# 这些是基本的校验规则。
ser = UserSerializer(data=request.data) # 不指定data会默认赋值给instance
#创建一个序列化对象,将data传进去
if ser.is_valid(): # 然后作判断,类似于form表单
# print(ser.validated_data['key'])
# print(ser.validated_data)
ser.save()
else:
print(ser.error_messages)
数据校验–钩子函数
# 局部钩子
def validate_name(self,value): # name是校验字段的名字,value是字段的额值
if 'j' in value.lower():
raise exceptions.ValidationError('名字中不能有j')
# raise exceptions.ValidationError({' 自定义键 ':' 自定义错误信息 '})
else:
return value
# 全局钩子 ,对于不需要入库的字段可以在全局钩子中,attrs.pop()出去,attrs中是入库字段。
def validate(self,attrs): # attrs保存所有字段的值
if attrs.get('pwd').lenght < 8:
raise exceptions.ValidationError('太短')
else:
return attrs
parse_classes = [JSONParse,FormParse]
roles = models.Role.objects.all()
pg = PageNumberPagination()
pg_roles = pg.paginate_queryset(queryset = roles,request = request,view=self)
ser = RolesSerializer(instance = pg_roles,many = True)
return pg.get_paginated_response(ser.data)
from rest-framework.routes import Simpleroute
route = Simpleroute()
# route.register('prefix', 'viewset')
route.register('user', 'UserViewset')
# 会自动生成2个路由,一个包含list方法,另一个包含delete,update等方法
url('',include(route.urls))
分页信息
,序列化信息
,以及要操作的序列
:queryset。roles = self.get_queryset()
pg_roles = self.paginate_queryset(roles)
ser = self.get_serializer(pg_roles,mant=True)
3. GenericViewSet: