可变类型和不可变类型
可变类型:列表 字典 集合
不可变类型:整型 浮点型 字符串 数组
常用的魔法方法
魔法方法就是在某种情况下会自动触发
__init__ 类()---->对象进行实例化
__new__ 类()---->产生一个空对象---->触发__init__完成对象实例化 并且有返回值 返回实例化好的对象
*** 两者的区别是:当实例化一个类的时候最先被调用的方法是__new__方法,__init__是在类实例被创建出来之后调用的,它完成的是类实例的初始化操作,而__new__方法是创建这个类实例的方法
__call__ 对象加括号调用时则会触发
__str__ 对象执行打印操作会触发 这个方法返回什么打印结果就是什么 并且这个方法必须返回一个字符串类型的数据
__getattr__ 对象.属性 属性不存在 则会触发
__setattr__ 对象.属性=值 则会触发
__getitem__ 对象['属性'],属性不存在 则会触发
__setitem__ 对象['属性']=值 则会触发
# 双下enter(enter) 和 双下exit(exit)必须一起使用 要不会报错
__enter__ 当对象被当作with上下文管理操作的开始自动触发,并且这个方法返回什么,as后面的变量就会收到什么
__exit__ 当对象参与的with上下文管理语法执行完之后自动触发(子代码结束)
-上下文管理器:只要重写了__enter__和__exit__方法,就具备这个能力
with 对象 as xx:
1 写了一行代码,触发__enter__的执行
2 写了一行代码,触发__exit__,做一些资源清理工作
类中的装饰器用过哪些
如果类中不加任何装饰器,是绑定给对象的方法,对象来调用,对象 点 直接调用,会自动把对象传进去
classmethod 绑定给类的方法,类来调,会自动把类传进去,对象也可以调
staticmethod 不绑定给任何东西,也叫静态方法,类可以调 对象也可以调,它们不会自动传值
# 如何把方法包装成数据属性
property装饰器
加property装饰器的功能就是将函数的返回值作为类的属性,方法伪装成属性
意思就是我们调用这个被装饰的函数不需要加括号去运行,而是直接像变量或者属性一样获取这个值
类中如何隐藏属性
__属性,方法
高匿代理和透明代理
# 高匿代理和透明代理
-高匿:服务端拿不到真实客户端的ip地址
-透明:服务端能拿到真实客户端的ip地址
后端如何拿到真实客户端ip地址
X-Forwarded-For(XFF):获取HTTP请求真实的IP(作用)
通过HTTP代理或负载均衡方式连接到Web服务器的客户端最原始的IP地址的HTTP请求头字段
什么是http
超文本传输协议 信息是明文传输
是一个简单的请求——响应协议
设计 HTTP 最初的目的是为了提供一种发布和接收 HTML 页面的方法。它可以使浏览器更加高效
http版本区别
0.9:底层基于tcp,每次http请求,都是建立一个tcp连接,三次握手,请求结束需要四次挥手
1.1:请求头中有个参数Keep-alive,可以保证多个http请求公用一个TCP连接
2.x:多路复用,多个请求使用同一个数据包
http和https的区别
hhtp是http+ssl/tls
http:
超文本传输协议,是互联网上应用最为广泛的一种网络协议
https:
是具有安全性的ssl加密传输协议,为浏览器和服务器之间的通信加密,确保数据传输的安全
SSL 协议位于 TCP/IP 协议与各种应用层协议之间,为数据通讯提供安全支持。SSL 协议可分为两层:
SSL 记录协议,它建立在可靠的传输协议(如TCP)之上,为高层协议提供数据封装、压缩、加密等基本功能的支持。
SSL 握手协议,它建立在 SSL 记录协议之上,用于在实际的数据传输开始前,通讯双方进行身份认证、协商加密算法、交换加密密钥等
区别:
1 https协议需要CA申请证书,一般免费证书较少,因而需要一定费用
2 http是超文本传输协议,信息是明文传输,https则是具有安全性的SSL加密传输协议
3 连接方式不同,http连接是无状态 https是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议 比http安全
4 http和https使用的是完全不同的连接方式,用的端口的也不一样,http是80,https是443
4 http页面响应速度比https快,因为http使用TCP三次握手建立连接,客户端和服务器需要交换 3 个包,而https除了TCP的三个包,还要加上ssl握手需要的 9 个包,所以一共是 12 个包
http请求头
x-forword-for,user-agent(用户代理),cookie,referer(告诉服务器该网页是从哪个页面链接过来的),content-Type(编码)
http协议
HTTP协议:是W3C制定的一种超文本传输协议
底层是TCP/IP
-请求协议:
请求首行:请求头地址,请求方式,http的版本
请求头:key-value
请求体
-响应协议:
响应首行:响应状态码,响应字符串描述
响应头:key-value,响应状态码,cookie
响应体
http协议特性
1 基于TCP/IP协议之上的应用层协议
2 基于请求/响应模式
3 无状态保存
4 无连接
http的get请求和post请求的区别
POST 和GET本质都是一样的
最直观的区别就是get把参数包含在url中
post通过request body传递参数
你要给get加上request body,给post带上url参数,技术上是完全行的通的
1 get产生一个TCP数据包,post产生两个TCP数据包
对于get方式的请求,浏览器会把http header和data一并发送出去,服务器响应200(返回数据)
而对于post,浏览器先发送header,服务器响应100 continue,浏览器再发送data,服务器响应200(返回数据)
2 get请求在浏览器刷新或者退出的时候是无害的,post的话数据会被重新提交
3 get可以存在缓存中,而post不行
4 get请求无法发送大量数据,而post可以
5 get请求只能进行url编码,而post支持多种编码方式
6 对参数的数据类型,get只接受ASCII字符,而post没有限制
7 get比post更不安全,因为参数直接暴露在URL上,所以不能用来传递敏感信息
8 get可以被书签收藏,post不行
面试官要看项目
看的是你的编码水平
公司的看不了,给他看的都是个人项目
公司项目看不了,签了保密协议
数据库如何处理的?
-云数据库:阿里云数据库,花钱买的服务--->给账号密码--->公司不需要自己搭建mysql
-MySQL
-Reids
-MogoDB
-自己的数据库,部署在云服务器上的数据库,是你自己的
你用过什么云产品?
阿里云的ecs,服务器
阿里云的oss,对象存储
云短信
七牛云 文件存储
看看你的数据库
-配置文件 dev.py 连的是本地的127.0.0.1
因为是自己的项目写一些小项目想用来开源 连的都是本地 自己做测试用的
上线怎么弄
不知道,公司就是给我一个地址和端口号,账名和密码不知道
账号和密码是在环境变量里去取的 这是他们上线的人才知道的 我们是不知道的
上线的数据库服务和项目服务是在同一台机器上吗?
不是的
django+uwsgi服务 一台
MySQL服务 单独一台 我们使用地址和端口号去链接 账号和密码是在环境变量里去取的 这是他们上线的人才知道的 我们是不知道的
redis 一台
前端服务 一台
是的
我们用户量不大 所有服务都在一台机器上 虽然这样不好 但公司就是这么用的 因为公司可能没有吧
celery用过吗?
用过
异步任务
延迟任务
定时任务
是可以用来做来定时任务,但是我们一般不采纳,因为celery太过庞大,就像用django写小脚本一样
深浅拷贝
浅拷贝:
会在内存中新开辟一个空间,存放这个拷贝的列表,但是列表里面的内容还是沿用之前对象的内存地址
深拷贝:
会在内存中开辟新空间,将原列表以及列表里面的可变数据类型重新创建一份,不可变数据类型则沿用之前的
总结:
1 浅拷贝花费时间少,占用的内存少,只拷贝顶层数据,拷贝效率高
2 对不可变对象拷贝时,浅拷贝和深拷贝的作用是一致的,不开辟新空间,相当于赋值操作
3 可变对象浅拷贝时,只拷贝第一层中的引用,如果元素是可变对象,并且被修改,那么拷贝的对象也会发生变化
4 可变对象深拷贝时,会逐层进行拷贝,遇到可变类型,就开辟一块内存复制下来
什么是生成器,有什么应用场景?
生成器是一个特殊的程序,可以被用作控制循环的迭代行为,是一边循环一边计算的机制,称为generator
生成器是迭代器的一种,使用yield返回值函数,每次调用yield会暂停,而可以使用next()函数和send()函数恢复生成器
生成器的特点:
1 节约内存
2 迭代到下一次的调用时,所使用的参数都是第一次所保留下的,就是说,在整个所有函数调用参数都是第一次所调用时保留的,而不是新创建的
3 send能穿值,能改变值
4 yield会阻塞,下次调用会接着上面的未执行的接着执行
应用场景:
由于可以使用生成器很方便地实现一个迭代器,因此迭代器适用的场景生成器几乎都适用
进行大文件的读取时,多任务,python的协程
双写一致性
1 修改数据库 更新缓存
2 修改数据库 删除缓存
3 定时更新
断点续传
# 概念
客户端的断点续传指的是在下载或者上传文件的时候,将下载或者上传的文件人为的划成几个部分,每一个部分采用一个线程进行上传或者下载,如果碰到网络故障,可以从已经上传或下载的部分开始继续上传下载未完成的部分,而没不要从头再下载,可以节省时间提高速度
-迅雷多线程下载
# 详细
大文件切片上传:
想要上传大文件,那么就需要将大文件进行切片,分为一块一块的小文件,将小文件分别发送给后台,后台接收到小文件后,将小文件存储起来,当所有小文件上传成功后,再将小文件合并到一个文件中,这样就完成了大文件切片上传
断点续传:
想要实现断点续传,那么首先实现大文件上传。当我们将文件切片,除了需要给文件对应的值(使用md5)外,还需要给每个小文件对应的编号,将md5和编号同时传送给后端,后端根据md5和编号进行小文件存储,当发生文件上传一半终止上传后,获取后端已经上传的小文件编号发送到前端,前端检测到后端发送的小文件编号,从对应编号的切片进行上传
内网穿透
内网穿透就是使用一台有公网 IP 的电脑(frp服务器,下文称为节点)作为 “中间人” 来与没有公网IP的电脑建立连接并转发数据
https://zhuanlan.zhihu.com/p/370483324
缓存穿透,击穿,雪崩
# 缓存穿透
是指查询一个缓存中和数据库中都不存在的数据,导致每次查询这条数据都会透过缓存,直接查库,最后返回空。当用户使用这条不存在的数据疯狂发起查询请求的时候,对数据库就造成的压力就非常大,甚至可能直接挂掉
# 解决缓存穿透的方法一般有两种:
第一种:缓存空对象
就是当数据库中查不到数据的时候,缓存一个空对象,然后给这个空对象的缓存设置一个过期时间,这样下次再查询该数据的时候就可以直接从缓存中拿到,从而达到减少数据库压力的目的
缺点:
1 需要缓存层提供更多的内存空间来缓存这些空对象,当空对象很多的时候就会浪费很多内存
2 会导致缓存层和存储层数据不一致
第二种:使用布隆过滤器(推荐方法)
就是一种数据结构,它是由一个长度为m bit的位数组与n个hash函数组成的数据结构,位数组中每个元素初始值都为0,在初始化布隆过滤器时,会先将所有key进行n次hash运算,这样就可以得到n个位置,然后将这n个位置上的元素改为1,这样,就相当于把所有的key保存到了布隆过滤器中了
布隆过滤器:
bloomfilter:是一个通过多哈希函数映射到一张表的数据结构,能够快速的判断一个元素在一个集合内是否存在,具有很好的空间和时间效率。(典型例子,爬虫url去重)
# 缓存击穿
是指当缓存中某个热点数据过期了,在该热点数据重新载入缓存之前,有大量的查询请求穿过缓存,直接查询数据库。这种情况会导致数据库压力瞬间骤增,造成大量请求阻塞,甚至直接挂掉
# 解决缓存击穿的方法也有两种:
第一种:设置key值永不过期
在设置热点key的时候,不给key设置过期时间即可,还有另外一种方式也可以达到key不过期的目的,就是正常给key设置过期时间,在后台同时启一个定时任务去定时更新这个缓存
第二种:使用分布式锁
使用了加锁的方式,锁的对象就是key,这样,当大量查询同一个key的请求并发进来时,只能有一个请求获取到锁,然后获取到锁的线程查询数据库,然后将结果放入到缓存中,然后释放锁,此时,其他处于锁等待的请求即可继续执行,由于此时缓存中已经有了数据,所以直接从缓存中获取到数据返回,并不会查询数据库
# 缓存雪崩
是指当缓存中有大量的key值在同一时间过期,或者Redis直接宕机了,导致大量的查询请求全部到达数据库,造成的数据库查询压力骤增,甚至直接挂掉
# 缓存雪崩解决方案
第一种:key值过期时间打散
将每个key值的过期时间打散那,使它们失效点尽可能均匀分布
第二种:redis发生故障的情况
署redis时可以使用redis的几种高可用方案部署
-redis主从架构
-redis哨兵机制
-redis集群
http/2多路复用
HTTP/1下的请求,并不能很好的利用宽带,一个TCP连接同时只能有一个HTTP请求和响应,如果正在发送一个HTTP请求,那其他的HTTP请求就得排队
这种排队会产生一个请求队列,当队头的请求发生意外(比如丢包、服务器响应缓慢),导致比平时要慢得多,就会导致后面的请求被延迟。这种情况我们称为 队头阻塞
为了解决这个问题HTTP/2采用了多路复用
在HTTP/2中,有两个非常重要的概念,分别是帧(frame)和流(stream)
帧代表着最小数据单位,每个帧会标识出该帧属于哪个流,流也就是多个帧组成的数据流
HTTP/2采用二进制数据帧传输,取代了HTTP1.x的文本格式,二进制格式解析更高效
# 多路复用
多路复用代替了HTTP1.x的序列和阻塞机制,所有相同的域名请求都通过同一个TCP连接并发完成
同一TCP中可以发送多个请求,对端通过帧中的标识知道属于哪个请求,通过 这个技术,可以避免HTTP旧版本中的队头阻塞问题,极大的提高传输性能
IO多路复用
数据库三大范式
1 . 每一个字段的值必须具有原子性,字段是最小的单元不可再分
第一范式(1NF):是指数据库表的每一列都是不可分割的
表中不能嵌套表,要保证数据不可再分
例如:姓名,可以分为 姓 和 名 这是不允许的
- 表中字段必须完全依赖于全部主键而非部分主键
第二范式(2NF):如果表是单主键,那么主键以外的列必须完全依赖于主键,如果表是复合主键,那么主键以外的列必须完全依赖于主键,不能仅依赖主键的一部分
任意 一个字段都只能依赖表中的同一个字段
例如:一个表中有学生ID和学生姓名,班级ID和班级名称 这个就不符合第二范式
- 非主键外所有字段都必须互不依赖
第三范式(3NF):表中的非主键列必须和主键之间相关而不能间接相关,也就是说:非主键列之间不能相关依赖
一张表最多存在在2层同类型信息
例如:一张表中有学生编号和学生名称,有班级ID和院系名称,那么学生可以确定班级,班级可以确定院系,那么院系和学生有传递依赖,所以不符合第三范式
MySQL有哪些索引类型,分别有什么作用
索引:索引是提高检索速度的
MySQL有哪些类型的索引
目前MySQL主要有FULLTEXT
,HASH
,BTREE
,RTREE
FULLTEXT
FULLTEXT全文检索,目前只有MySAM引擎支持
这类针对于文本的模糊查询效率较低问题
HASH
由于HASH的唯一及类似键值对的形式
可以一次定位,不需要像树形索引那样逐层查找,因此具有较高的效率。但是这种高效是有条件的,即只在'='和'in'条件下高效,对于范围查找、排序及组合索引仍然效率不高
BTREE
BTREE索引就是一种将索引值按一定的算法,存入一个树形结构中,每次查询都是从树的入口开始遍历。这是mysql里默认和最常用的索引类型
RTREE
RTREE在MySQL很少使用,仅支持geometry数据类型,支持该类型的存储只有MySAM、BDb、innoDb,NDb、Archive几种。相对于BTREE,RTREE的优势在于范围查找
索引种类
聚簇索引,聚集索引,主键索引,主键,如果不存在主键,隐藏一个主键,构建聚簇索引:
加速查询 + 列值唯一(不可以有null) + 表中只有一个组合索引,其效率大于索引合并
辅助索引 普通索引 index:
仅加速查询
唯一索引 unique:
加速查询+列值唯一
联合索引,组合索引,多列索引:unique_to
常见的索引数据结构
哈希表,有序数组,二叉树
哈希表
对索引的key进行一次hash计算就可以定位出数据存储的位置
很多时候hash索引要比B+Tree索引更高效
有序数组
有序数组在范围查找中可以使用二分法,能很大程度的缩短查询时间
二叉树
右侧元素大于父元素数据,左侧数据小于父元素数据
mysql使用的是b-Tree
b-Tree:
非叶子节点不存储data,只存储索引,目的是可以放更多的索引
叶子节点包含所有索引字段
叶子节点使用指针连接,提高期间访问的性能
事务 事务特性和隔离级别
什么是事务
在MySQL中事务是一种机制,一个操作序列,是访问和更新数据库的执行单元,事务里会包含一个或多个数据库的操作命令,当所有命令作为一个整体一起向系统提交或者撤销操作请求的时候,那么这一组数据库的命令要都执行,要么都不执行,这就牵扯到了事务的四大特性,这就是原子性
事务的特性
1. 原子性
就是事务所包含的所有操作,要么全部成功,要么全部失败
2. 一致性
事务执行前和执行后,必须保持一致
3. 隔离性
一个事务执行的过程中,并不能被别的事务干扰
4. 持久性
事务被提交后,对数据的修改是永恒的,即使是出现故障也能够正常保持
事务的隔离级别
1. Serialzable(可串行化):可避免脏读、不可重复读、幻读的发生
2. Repeatable(可重复读):可以避免脏读、不可重复读的发生
3. Read committed(读已提交):可避免脏读的发生
4. Read uncommited(读未提交):最低级别,任何情况都无法保证
MySQL默认的是 Repeatable,oracle默认的是Read commited
MySQL支持四种隔离级别,而oracle支持Serialzable和Read committed这两种级别
脏读
脏读指的是读到了其他事务未提交的数据,未提交意味着这些数据可能会回滚,也就是可能最终不会存到数据库中,也就是不存在的数据。读到了并不一定最终存在的数据,这就是脏读
不可重复读
不可重复读指的是在一个事务内,最开始读到的数据和事务结束前的任意时刻读到的同一批数据出现不一致的情况
例如:
事务 A 多次读取同一数据,但事务 B 在事务A多次读取的过程中,对数据作了更新并提交,导致事务A多次读取同一数据时,结果 不一致
幻读
幻读并不是说两次读取获取的结果集是不同的,幻读侧重的方面是某一次的 select 操作得到的结果所表征的数据状态无法支撑后续的业务操作
更为具体一些:select 某记录是否存在,不存在,准备插入此记录,但执行 insert 时发现此记录已存在,无法插入,此时就发生了幻读
查的时候明明没有这条记录,但插入的时候 却告诉我 主键冲突,这就好像幻觉一样。
这才是所有的幻读。 不可重复读侧重表达 读-读,幻读则是说 读-写,用写来证实读的是鬼影
mysql5.7以后默认隔离级别是什么?
REPEATABLE-READ(可重复读)
在开始读取数据(事务开启)时,不再允许修改操作,这样就可以在同一个事务内两次读到的数据是一样的,因此称为是可重复读隔离级别,但是有时可能会出现幻读
什么是qps,tps,并发量,pv,uv
1 QPS
Queries Per Second,每秒查询率,一台服务器每秒能够响应的查询次数
2 TPS
Transactions Per Second,是每秒处理的事务数,包括一条消息入和一条消息出,加上一次用户数据库访问
3 并发量
系统同时处理的请求或事务数,可以直接理解为:系统同时处理的请求数量
4 PV
PV(Page View):页面访问量,即页面浏览量或点击量,用户每次刷新即被计算一次。可以统计服务一天的访问日志得到。
5 UV
UV(Unique Visitor):独立访客,统计1天内访问某站点的用户数。可以统计服务一天的访问日志并根据用户的唯一标识去重得到。
什么是接口幂等性问题,如何解决?
什么是接口幂等性问题
什么是幂等性?
幂等性是系统服务对外一种承诺,承诺只要调用接口成功,外部多次调用对系统的影响是一致的。声明为幂等的服务会认为外部调用失败是常态,并且失败之后必然会有重试
接口幂等性
接口幂等性就是用户对同一操作发起了一次或多次请求的对数据的影响是一致不变的,不会因为多次的请求而产生副作用
什么是接口幂等性问题?
接口幂等性问题是指在多次调用同一个接口时,可能会产生重复的操作,导致系统数据异常或出现重复操作的情况
解决接口幂等性问题
token机制
1、服务端提供了发送token的接口。我们在分析业务的时候,哪些业务是存在幂等问题的,就必须在执行业务前,先去获取token,服务器会把token保存到redis中。
2、然后调用业务接口请求时,把token携带过去,一般放在请求头部。
3、服务器判断token是否存在redis中,存在表示第一次请求,然后删除token,继续执行业务。
4、如果判断token不存在redis中,就表示是重复操作,直接返回重复标记给client,这样就保证了业务代码,不被重复执行。
关键点 先删除token,还是后删除token
后删除token:如果进行业务处理成功后,删除redis中的token失败了,这样就导致了有可能会发生重复请求,因为token没有被删除。这个问题其实是数据库和缓存redis数据不一致问题,后续会写文章进行讲解。
先删除token:如果系统出现问题导致业务处理出现异常,业务处理没有成功,接口调用方也没有获取到明确的结果,然后进行重试,但token已经删除掉了,服务端判断token不存在,认为是重复请求,就直接返回了,无法进行业务处理了。
先删除token可以保证不会因为重复请求,业务数据出现问题。出现业务异常,可以让调用方配合处理一下,重新获取新的token,再次由业务调用方发起重试请求就ok了。
token机制缺点
业务请求每次请求,都会有额外的请求(一次获取token请求、判断token是否存在的业务)。其实真实的生产环境中,1万请求也许只会存在10个左右的请求会发生重试,为了这10个请求,我们让9990个请求都发生了额外的请求。
乐观锁机制
这种方法适合在更新的场景中
update t_goods set count = count -1 , version = version + 1 where good_id=2 and version = 1
根据version版本,也就是在操作库存前先获取当前商品的version版本号,然后操作的时候带上此version号。我们梳理下,我们第一次操作库存时,得到version为1,调用库存服务version变成了2;但返回给订单服务出现了问题,订单服务又一次发起调用库存服务,当订单服务传如的version还是1,再执行上面的sql语句时,就不会执行;因为version已经变为2了,where条件就不成立。这样就保证了不管调用几次,只会真正的处理一次。
乐观锁主要使用于处理读多写少的问题
唯一主键
这个机制是利用了数据库的主键唯一约束的特性,解决了在insert场景时幂等问题。但主键的要求不是自增的主键,这样就需要业务生成全局唯一的主键。
如果是分库分表场景下,路由规则要保证相同请求下,落地在同一个数据库和同一表中,要不然数据库主键约束就不起效果了,因为是不同的数据库和表主键不相关。
防重表
使用订单号orderNo做为去重表的唯一索引,把唯一索引插入去重表,再进行业务操作,且他们在同一个事务中。这个保证了重复请求时,因为去重表有唯一约束,导致请求失败,避免了幂等问题。这里要注意的是,去重表和业务表应该在同一库中,这样就保证了在同一个事务,即使业务操作失败了,也会把去重表的数据回滚。这个很好的保证了数据一致性。
唯一ID
调用接口时,生成一个唯一id,redis将数据保存到集合中(去重),存在即处理过。
**唯一ID机制 **
调用者生成一个唯一ID
前端
前端:按钮只能点击一次
什么是gil锁,有什么作用
首先明确gil并不是python的特性,它是在实现python解释器(Cpython)时引入的一个概念
GIL:全局解释器锁
GIL本质也是一把互斥锁
作用:用来阻止同一个进程内多个线程同时执行
原因:因为CPython解释器中的垃圾回收机制不是线程安全的
GIL只能够确保同进程内多线程数据不会被垃圾回收机制弄乱
并不能确保程序里面的数据是否安全
python的垃圾回收机制是什么样的
Python的垃圾回收,其实高级的语言都有自己的垃圾回收机制简称GC
Python 的GC模块主要运用了
引用计数来跟踪和回收垃圾,通过“标记-清除”解决容器对象可能产生的循环引用问题,通过分代回收以空间换时间进一步提高垃圾回收的效率
也就是采用“引用计数“为主(实时性,一旦没有引用,内存就直接释放了),“标记-清除”与“分代收集”两种机制为辅的策略
引用计数
为每一个对象维护一个引用计数器,当一个对象的引用被创建或者复制时,(对象的引用)计数器+1,当一个对象的引用被销毁时,计数器的值-1,当计数器为0时,就意味着对象已经再没有被使用了,可以将其内存释放掉
标记清除
标记清除的出现打破了循环引用,也就是它只关注那些可能会产生循环引用的对象,当内存占用达到临界值的时候,程序会自动停止,然后扫描程序中所有的数据,并给只产生循环引用的数据打上标记,之后一次性清除
分代回收
活的越长的对象,就越不可能是垃圾,就应该减少对它的垃圾收集频率
解释为什么计算密集型用多进程,io密集型用多线程
计算密集型:又称cpu密集型
系统运行CPU读写I/O(硬盘/内存)时可以在很短的时间内完成,几乎没有阻塞(等待I/O的实时间)时间,而CPU一直有大量运算要处理,因此CPU负载长期过高
io密集型
系统运行多是CPU在等I/O (硬盘/内存)的读写操作,此类情景下CPU负载并不高
# 为什么计算密集型用多进程?
-由于GIL锁的存在,即便是多核机器,同一时刻,也只能有一个线程在执行
-线程需要cpu去调度执行
-如果开了多线程,是计算密集型,计算是消耗cpu,假设是四核电脑,不能充分利用这四个核,只能有一个核在执行,开多线程没有用
-而如果计算密集型,开了多进程,gil是在cpython解释器进程中的,再进程中开启线程执行计算,可以充分利用多核优势
-开了一个进程,就开了一个cpython解释器的进程,就会有一个gil锁
-由于GIL锁的存在,如果是计算密集型,开启多线程,不能利用多核优势,
-开启多进程,可以利用多核优势
但也分为单个cpu和多个cpu的情况
单个cpu的情况的话还是多线程有优势 消耗的资源较少 多进程还要申请额外的空间,会消耗更多的资源
但多个cpu的话多进程是完胜的,总耗时是单个进程的耗时,因为多个进程同时进行,多线程的话就是多个进程的综合时间
# 为什么io密集型用多线程?
假设我们有多个线程都在发网络请求(request,response),一个请求的从发出到接收的过程中cpu大多时间都是在等。
所以,当前线程发出请求后,由于不占用cpu资源,可以阻塞等待,然后cpu执行权可以被另外一个线程所享有去发网络请求
当线程等待时间所占比例越高,需要越多线程,启用其他线程继续使用CPU,以此提高CPU的利用率
本身开启进程是非常消耗资源的,如果是io密集型,没有必要开多进程,并不会有显著的效率提升
进程
进程是资源分配的最小单位,一个应用程序运行,至少会开一个进程
代码实现
使用multiprocessing模块
Process类
方式一:
用函数创建进程
process加括号产生一个对象
对象点start()
方式二:
自己写一个类继承Process
重写run方法
用自己的类实例化得到一个对象
然后start()
线程
线程是cpu调度的最小单位,cpu执行的最小单位
代码实现
使用threading模块
Thread类
方式一:
用函数创建进程
Thread加括号产生一个对象
对象点start()
方式二:
自己写一个类继承Thread
重写run方法
用自己的类实例化得到一个对象
然后start()
协程
单线程下实现并发,代码层面遇到io,自己切换
代码实现
早期之前:借助于第三方gevent,基于greelet写的
async 和 await 关键字,不借助于第三方,开启协程asyncio 包
-必须写在一个函数前, async def task()--->这个函数执行的结果是协程函数
-await 只要是io操作的代码,前面必须加 await
什么是猴子补丁,有什么用途
猴子补丁(Monkey Patch)
在程序运行的过程中,动态替换的一种技术
1 gevent--->猴子补丁--->monkey.patch_all()--->动态的替换内部 会阻塞的代码
time
socket
同步代码:io操作,会阻塞,会释放gil锁,这条线程就会被释放掉cpu的执行
异步代码:遇到io,不释放gil锁
把所有内置的会释放gil锁的模块,动态替换称gevent自己写的,不会释放gil锁的模块
动态的把所有同步的代码,都替换成异步代码,把所有会阻塞的代码,都替换
2 比如咱们的json模块,用内置的json效率低,有一个第三方的ujson模块
-不改变原来程序代码,把程序中所有json都替换成ujson
-在程序入口处:import ujson as json
3 django中 pymysql的替换
import pymysql
pymysql.install_as_mysqlDB()
进程、线程、协程 你在哪里用过
-我一般遇到计算密集型操作,我会开多进程,io密集型的操作我一般开多线程
-闲来无事,爬取别人的数据,喜欢开多线程,爬虫的io居多
-程序中,异步做一件事情,也可以开多线程
比如一个视图函数,异步的把数据写进文件中
异步的发送钉钉通知
异步的发送邮件
-但实际上,在项目中,不需要我们开启进程线程,可以借助于第三方框架比如celery就可以做异步操作
而celery的worker,就是进程线程架构
-django框架,是支持并发的,我们没有开启多进程和多线程,但是符合uwsgi的,web服务器在进入django框架之前,开启了进程和线程来执行视图函数
为什么有了gil锁还要互斥锁
GIL只能保证进程内多线程数据不会被垃圾回收机制弄乱并不能确保程序里面的数据是否是安全的
gil锁:
全局解释器锁,线程要执行,必须先获得到gil锁,才能执行
互斥锁:
为了保证多线程并发操作数据(变量)而设置的锁,保证在加锁和释放锁之间,其他线程不能操作
gil本质上也是一个大的互斥锁
# 出现了数据错乱,出现了多条线程操作变量,出现的并发安全问题
a=0
线程1要计算:a+=1
1 线程1 拿到gil
2 读取a=0
3 假设时间片到了,释放gil,释放cpu
4 等待下次被调度执行
10 轮到它了,获取gil锁
11 继续往下执行:计算a+1
12 把结果赋值给a,a=1
13 释放gil锁
线程2要计算: a+=1
5 线程2获得了gil锁
6 读取a=0
7 计算a+1
8 把结果赋值给a a=1
9 释放gil锁
# 什么是临界区?处在出现并发安全问题的这段代码称之为临界区,临界区会出现并发安全问题,所以要在临界区加锁
# 加锁
6 读取a=0
7 计算a+1
8 把结果赋值给a,a=1
# 释放锁
# 互斥锁保证数据安全
a=0
线程1要计算:a+=1
1 线程1 拿到gil
# 加锁
2 读取a=0
3 假设时间片到了,释放gil,释放cpu
4 等待下次被调度执行
7 轮到它了,获得gil锁
8 继续往下执行:计算a+1
9 把结果赋值给a a=1
10 释放gil锁
线程2要计算: a+=1
5 线程2 获得了gil锁
# 获得锁,获得不到
6 释放gil锁
11 获得gil锁
# 加锁
12 读取a=0
13 计算a+1
14 把结果赋值给a,a=1
15 释放锁
16 释放gil锁
# gil锁并锁不住临界区,临界区需要我们自己用互斥锁加锁
鸭子类型
鸭子类型:
只要看上去像鸭子 走路像鸭子 说话像鸭子,我们就可以叫它鸭子
解释:鸭子类型是python面向对象中描述接口的一个概念,区分与其他编程语言
比如java:实现接口,必须显示的继承一个接口
而python:实现接口,遵循鸭子类型,不需要显示继承一个接口(类),只要类中有对应的属性和方法,我们就称这几个类的对象为同一个种类型
并发 并行
并发
同一时间段内,执行多个任务的能力
并行
同一时间,执行多个任务的能力
# 并行必须是多cpu支持
同步 异步
# 程序调用的角度
同步:
同步是一件事一件事的做;只有执行完前一个任务,才会执行下一个任务。同步意味着有序
异步:
当一个任务已经执行完,你无需等待任务执行完成,就可以换到另一个任务上,异步意味着无序
阻塞 非阻塞
# 程序执行的角度
阻塞:
程序在等待某个操作完成期间,自身无法继续干别的事情,则称该程序在该操作上是阻塞的
非阻塞:
程序在等待某操作过程中,自身不被阻塞,可以继续运行干别的事情,则称该程序在该操作上是非阻塞的
django flask高并发部署
django flask 同步框架,部署的时候,使用uwsgi部署,uwsgi是进程线程架构,并发量不高
可以通过uwsgi+gevent,部署成异步程序
什么是反射,python中如何使用反射
# 反射的含义
专业的解释:指程序可以访问、检测和修改本身状态或者行为的一种能力
白话:可以理解为利用字符串的形式去对象中操作成员属性和方法
# python中使用反射
hasattr()
判断字符串在对象obj中是否存在(属性,方法),存在返回True,不存在返回Flase
getattr()
根据字符串在对象obj中查找 如找到同名属性,则返回属性,如找到同名方法,则返回方法的引用
如果未能找到同名的属性或者方法,则抛出异常:AttributeError
setattr()
字符串给对象设置键值对(名称空间中的名字)
delattr()
字符串删除对象对应的键值对(名称空间中的名字)
将你输入的字符串str在对象obj中查找,如找到同名属性或者方法就进行删除
从浏览器输入一个地址,到看到页面信息,经历的过程
1 从浏览器中输入的是:域名--->要做域名解析--->把域名解析成ip地址+端口的形式--->dns解析--->(浏览器缓存,本机缓存,host文件,上一级递归解析服务,13台根dns)--->如果解析不到--->页面就会报错
2 解析完之后,向解析出的域名和端口号建立TCP连接,进行3次握手
3 向某个地址发送http的get请求 (可拓展http协议 请求头等)
4 如果后端服务是nginx转发,nginx把http请求转发给web框架(django,flask) --->django请求生命周期
5 后端服务器以http响应的形式返回给客户端浏览器
6 客户端浏览器把http响应体的内容展示在浏览器上,但http响应还有:
响应首行:响应状态码,响应字符串描述
响应头:key-value,响应状态码,cookie
响应体
7 四次挥手断开TCP连接--->http协议版本号
http版本区别
0.9:底层基于tcp,每次http请求,都是建立一个tcp连接,三次握手,请求结束需要四次挥手
1.1:请求头中有个参数Keep-alive,可以保证多个http请求公用一个TCP连接
2.x:多路复用,多个请求使用同一个数据包
多路复用解释:
HTTP/1下的请求,并不能很好的利用宽带,一个TCP连接同时只能有一个HTTP请求和响应,如果正在发送一个HTTP请求,那其他的HTTP请求就得排队
这种排队会产生一个请求队列,当队头的请求发生意外(比如丢包、服务器响应缓慢),导致比平时要慢得多,就会导致后面的请求被延迟。这种情况我们称为 队头阻塞
为了解决这个问题HTTP/2采用了多路复用
在HTTP/2中,有两个非常重要的概念,分别是帧(frame)和流(stream)
帧代表着最小数据单位,每个帧会标识出该帧属于哪个流,流也就是多个帧组成的数据流
HTTP/2采用二进制数据帧传输,取代了HTTP1.x的文本格式,二进制格式解析更高效
# 多路复用
多路复用代替了HTTP1.x的序列和阻塞机制,所有相同的域名请求都通过同一个TCP连接并发完成
同一TCP中可以发送多个请求,对端通过帧中的标识知道属于哪个请求,通过 这个技术,可以避免HTTP旧版本中的队头阻塞问题,极大的提高传输性能
左连接,右连接,内连接,全连接:MySQL不能直接支持
数据通常不在同一张表中,这就涉及到连表操作,而表间连接方式有很多
左连接:
以左表为基准 展示左表所有的数据 如果没有对应项则用NULL填充
右连接:
以右表为基准 展示右表所有的数据 如果没有对应项则用NULL填充
内连接:
把两张表中共有的数据,连接到一起
全连接:
以左右表为基准 展示所有数据 各自没有的全部NULL填充
union和union all的区别?
union就是全连接
select 出来结果,union,union all都是对结果进行合并,求并集
主要区别在于,union会自动去除重复行,而union all不会。因此,如果你想要完全保留所有的行,包括重复的行,可以使用union all
tcp 三次握手和四次挥手
三次握手
三次握手:建链接
第一次握手:我(客户端)发送一个链接请求,我这个请求报文的序号是x,并且等待服务器回应状态
第二次握手:我(服务器)答应连你,确认到你的信息了,下次你(客户机)给我发上次发送的报文(x)的后一个(就是x+1 因为这个我有了 我要下一个),我发送报文序号是y,并且进入等待客户机回应的状态
# 这个时候可能会发生洪水攻击 因为有可靠协议有反馈机制,客户端可能建立完第一次握手就跑了,而服务端在一直等待客户端的回应,并且在一定时间内反复发送请求与客户端建立连接的包,这时候大量的客户端都建立完第一次握手就跑路了,这时候就发生了洪水攻击
第三次握手:我知道你接受我的连接请求了,这次我发送的报文序号是(x+1),你下次给我发送y+1这个序号的报文
那为什么要三次握手呢?为什么不是两次握手呢?
为了阻止历史的重复连接初始化造成的混乱问题
防止使用 TCP 协议通信的双方建立了错误的连接
1.流式协议、可靠协议(数据不容易丢失)
造成数据不容易丢失的原因不是因为有双向通道,而是因为有反馈机制
给对方发消息之后会保留一个副本 直到对方回应消息收到了才会删除
否则在一定时间内反复发送
2.洪水攻击
同一时间有大量的客户端请求建立连接 会导致服务端一直处于SYN_RCVD状态,服务端接收到了大量的syn请求,处于rcvd状态
3.服务端如何区分客户端建立链接的请求
可以对请求做唯一标识
四次挥手
四次挥手:断链接
第一次挥手:客户端发送了一个断开连接的标志,并说这个报文是序号是u,接着客户端就进入了等待服务器确认的状态
第二次挥手:服务器说我确认收到你的断开请求了,我发送的报文序号是v, 下次你(客户机)给我(服务器)发送u+1这个序号的报文,服务器开始处理剩余的一些数据
第三次挥手:服务器给客户机发送了一个断开连接的标志,确认收到你的断开请求了,我的发送的报文序号是w,你下次给我发送u+1这个序号的报文,因为在此之前客户端并没有再次的给服务器发送报文。所以服务器给客户机要的报文序号还是u+1
第四次挥手:客户机给服务器说我知道你可以断开了,我发送的报文的序号是u+1, 你(服务器)下次给我(客户端)发送的报文序号是w+1。服务器收到客户机的确认后 无需多言,就关闭连接了,客户机等了2MSL后服务器没有动静了就也关闭连接了
# 为什么客户端要等待2MSL的时间再关闭?
MSL: 最长报文段的寿命。那2MSL就是报文段一来一回的最长时间
1.四次不能合并为三次
因为中间需要确认消息是否发完
-SYN=1 表示要建立连接
-FIN:表示断开连接
-ACK:ACK=1 表示我收到了,允许
-seq:随机数,建立连接无论客户端还是服务端要建立连接就要要携带
-ack:回应请求就要加1返回
-三次握手:
-第一次:喂(SYN=1),我是lqz(seq=随机数)
客户端:SYN_SEND状态
服务端:没收到:listen 状态,收到了是:SYN_RCVD状态
-第二次:收到(ACK=1),lqz啊(ack=随机数+1),喂(SYN=1),我是刘亦菲(seq=随机数1)
服务端:SYN_RCVD状态
客户端:没收到服务端返回的第二次:SYN_SEND状态,一旦收到就是established
-第三次:收到(ACK=1),刘亦菲你好(ack=随机数1+1)
客户端:连接建好的状态 established
服务端:收到后,处于established
-大白话:三次握手
第一次:客户端向服务端发送建立连接请求,【携带一个随机数】(SYN=1,seq=随机数)
第二次:服务端回应客户端的建立连接请求(ACK=1,ack=随机数+1),服务端发送建立连接请求(SYN=1,seq=另一个随机数)
第三次:客户端回应服务端的建立连接请求(ACK=1,ack=另一个随机数+1)
# 四次挥手:
第一次:客户端向服务端发起断开连接的请求(FIN=随机数)
第二次:服务端收到后,回复这个请求(ACK=1,ack=随机数+1)
第三次:服务端向客户端发起断开连接的请求(FIN=另一个随机数)
第四次:客户端收到后,回复这个请求(ACK=1,ack=另一个随机数+1)
osi七层协议,哪七层,每层有哪些
应用层
表示层
会话层
传输层
网络层
数据链路层
物理连接层
# 物理连接层
主要用于确保计算机之间的物理连接介质 接收数据(bytes类型、二进制)
# 数据链路层
1.规定了电信号的分组方式
2.以太网协议
规定了计算机出厂都必须有一块网卡 网卡上有一串数字
该数字相当于计算机的身份证号都是独一无二的
该数字的特征:12位16进制数据
前6位产商编号 后6位流水线号
我们给这个独一无二的编号称之为'MAC地址/以太网地址'
# 网络层
IP协议:规定了所有接入互联网的计算机都必须有一个IP地址,类似于进场号(取决于网线,是可变的)
MAC地址是物理地址是永远无法改变的
IP地址是动态分配的 不同场所IP是不同的
IP地址特征:
IPV4:点分十进制
最小:0.0.0.0
最大:255.255.255.255
IPV6:十六进制
能给地球上每一粒沙分一个IP地址
# 传输层
PORT协议(端口协议)
用来标识一台计算机上面某一个应用程序
特征:动态分配
端口号范围:0-65535(也就是一台计算机可以一起运行65535个应用程序)
0-1024:系统常用端口号
1024-8000:常用软件端口号
TCP协议,UDP协议都属于传输层都是用来规定通信方式的
# 会话层
负责建立、管理和终止表示层实体之间的会话连接
# 表示层
数据的解码和编码、加密和解密、压缩和解压缩
# 应用层
应用层相当于是程序员自己写的应用程序 里面的协议非常的多
http(80) https(443) dns(53)之前都讲过都可拓展开来说
TCP和UDP的区别?
TCP与UDP都是用来规定通信方式的
区别:
TCP协议称之为流式协议、可靠协议(数据不容易丢失)
面向连接
UDP协议称之为数据报协议、不可靠协议
面向无连接
# UDP用在哪里?
早期的QQ使用的就是纯生的UDP协议
现在QQ自己添加了很多技术和功能
使用UDP的原因就是因为很简单 快捷 粗暴 只要指定对方的地址就可以发消息了 而TCP还要建立三握四挥
TCP我们可以打比方为打电话 你一句我一句
UDP我们可以看成是发短信 只要发了就行 不管对方看不看
什么是session和cookie 它俩的区别
cookie是存放再客户端浏览器里的键值对(只保留一个sessionID)
session是保存在服务端的的键值对
cookie执行原理:当客户端访问服务器时,服务器会生成一份cookie传给客户端,客户端会把这个cookie存起来,以后客户端每次访问服务器都会携带着这份cookie
session执行原理:当客户端第一次访问服务端,服务端会产生一个session,并且把这个session的id作为cookie给客户端,以后每次请求客户端浏览器每次都会携带者这份cookie来访问服务端(session的数据id)
区别:
一个是保存在客户端的,一个是保存在服务端的,然后cookie有大小限制不能超过4k,而session没有限制,cookie没有session安全,cookie只能存字符串,而session可以存任意类型
什么是jwt?
json web token(JWT) 就是web方向token的使用
token字符串分三段:
分为头(header),荷载(payload)和签名
头有一般有公司信息等等
荷载里放的是有效信息 过期时间 签发时间 用户id 用户信息等等
签名是第一部分和第二部分通过密钥+加密方式得到的
token的签发:
登陆成功后服务端生成,把token生成的字符串给前端,后面要前端发请求就要带着token字符串到后端
token的认证:
拿到第一部分和第二部分使用同样的加密方式+密钥再加密---》得到字符串和第三段作比较,如果一样表示没有被篡改,如果不一样就表示被篡改了,token就不能用了
什么是中间件
中间件是处于操作系统和应用程序之间的软件,中间件处于操作系统软件与用户的应用软件的中间所以叫中间件,中间件都要遵从底层协议或者自定义协议:TCP/IP协议
如何自定义中间件?
1 创建存储自定义中间件代码的py文件或者目录
2 参考自带中间件的代码编写类并继承
3 在类中编写五个可以自定义的方法
五个自定义方法:
# 说出来
process_request
1.请求来的时候会从上往下依次执行配置文件中注册的中间件 里面的process_request方法 如果没有则直接跳过
2.如果该方法自己返回了HttpResponse对象,那么请求不再继续往后直接返回响应数据
process_response
1.响应走的时候会从下往上依次经过每一个注册了的中间件 里面的该方法 如果没有则直接跳过
2.如果该方法自己返回了HttpResponse对象,那么响应会替换成该HttpResponse对象数据 而不再是视图函数想要返回给客户端的数据
# 忘记也可以
process_view
路由匹配成功之后执行视图函数/类之前自动触发
process_exception
视图函数/类执行报错自动触发(顺序同process_response)
process_template_response
视图函数/类返回的HttpResponse对象含有render并且对应一个方法的时候自动触发(顺序同process_response)
4 一定再配置文件中注册中间件才可以生效
django中间件
七个中间件
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware',
django生命周期
服务端浏览器发送请求到web网关服务,通过了web网关服务到中间件,中间件到路由层,路由层再到视图层,视图层从可以区模板层也能可以去模型层到模型层用ORM查询出数据库中的数据替换到模板层,再回视图层到中间件出去经过uwsgi返回给服务端浏览器
Vue生命周期
从vue实例创建开始到销毁,总共经历了八个生命周期钩子
beforeCreate
created
beforMount
mounted
beforUpdate
updated
beforDestory
destoryed
flask生命周期
WSGI uwsgi uWSGI,CGi,fastCGI 分别是什么?
WSGI协议
是为python语言定义的web服务器和web应用程序或者框架之间一种简单而通用的接口 其实就是定义了一种服务和应用程序解耦合的规范
uwsgi协议
uWSGI服务器实现的独有的协议,用于定义传输信息的类型,用于前端服务器与 uwsgi 的通信规范
uWSGI自有的一个协议
uWSGI:web服务器,等同于wsgiref
uwsgi:uWSGI自有的协议
uWSGI
符合WSGI协议的web服务器,用c写的,性能比较高,通常用来部署django,flask
CGi
通用网关接口,用于在web服务器和应用程序或者之间传递信息
fastcgi
是一种改进的CGi协议,用户提高web服务器和应用程序之间的通信效率和性能,与CGI不同的是,fastCGI可以通过保持一个长连接的方式重复利用已经创建的进程或线程,从而避免了多次启动和关闭的开销
符合WSGI协议的web服务器
# 符合WSGI协议的web服务器
wsgiref,werkzeug(一个是符合wsgi协议的web服务器+工具包(封装了一些东西))
uWSGI 是c语言写的,性能比较高
gunicorn:python写的
tornado:也可以部署django项目
web服务器到底是什么?服务器中间件
客户端(浏览器,app) 跟 服务端(web框架)之间的东西,服务器中间件
# nginx apache 是一类东西,就是做请求转发
# uWSGI gunicorn 只针对于python的web框架
# tomcat,jboss,weblogic 只针对java的web框架
如何自定制上下文管理器
一个对象如果实现了__enter__和__exit__方法,那么这个对象就支持上下文管理协议,即with语句
上下文管理协议适用于那些进入和退出之后自动执行一些代码的场景,比如文件、网络连接、数据库连接或使用锁、使用事务的编码场景等
# 如何使用
class ComonSession:
def __enter__(self):
print('进入with语句块时执行此方法,此方法如果有返回值会赋值给as声明的变量')
self.session=session
return self.session
def __exit__(self,exc_type,exc_val,exc_tb):
self.session.close()
with ComonSession() as session:
session.filter()
print('上下文管理器结束了')
python是值传递还是引用传递
# 严格意义上来说,python既不是值传递,也不是引用传递,python是自己的传递方式,规则是:
如果传递的是不可变类型,在函数中修改,就不会影响原来的变量
如果传递的是可变数据类型,在函数中修改,就会影响原来的变量,修改,而不是重新赋值
# python一切皆对象--->内部函数是一切皆引用(对象本质就是地址,就是引用)
# 什么是值,什么是引用
值就是一个变量=具体的值(一块内存空间放着这个变量的值)
引用是一个变量=内存地址(内存地址指向了值)
# 什么是值传递 什么是引用传递
如果是值传递,函数中修改了传递的值,不会影响原来的
如果是引用传递,函数中修改了传递的引用,就会影响原来的
什么是迭代器,生成器,装饰器
# 迭代:
一个不依赖于索引取值的方式,我们不需要关注它的位置,只要能够一个个取值,它就称之为迭代
for循环 next()
#可迭代对象:
可以迭代的(for,next取值的)python中的对象称之为可迭代对象,内置中有__iter__方法的都成为可迭代对象
字典,列表,字符串,元组,集合,文件对象
# 迭代器
迭代器要满足两个条件__iter__和__next__
调用可迭代对象,通过iter方法返回一个迭代对象,然后不断的用next方法返回元素(迭代对象的值),直到迭代完成后停止迭代
# 自定义迭代器:
写个类,类中重写__iter__和__next__方法,这个类的对象就是迭代器
# 生成器
生成器本质就是迭代器
生成器有两种:生成器表达式,生成器函数
生成器表达式:
生成器表达式使用元组,不过直接调用生成器表达式是无法得到对应值,需要用循环或者next方法才能够输出生成式表达式的值
生成器函数:
通过yield关键字来定义一个生成器函数,这个生成器函数返回值就是一个生成器对象,当循环一次后yield会进入挂起状态,然后再次循环的时候会从挂起的位置输出下一个结果
# 装饰器
在不改变被装饰对象原代码和调用方式的情况下给被装饰对象添加新的功能
# 闭包函数:
就是定义在函数内部的函数 并且用到外部函数名称空间中的名字
# 闭包和装饰器的区别:
闭包传递是变量,而装饰器传递的是函数除此之外没有任何区别,或者说装饰器是闭包的一种,它只是传递函数的闭包
# 在哪里用到过装饰器?
flask的路由就是装饰器
django的信号也可以用装饰器的方式注册
为接口记录访问日志
django的信号用过吗?如何用,干过什么
django提供的一种信号机制,其实就是观察者模式,又叫发布-订阅。当发生某种变化的时候,通知某个函数执行
# 内置信号:
如果是内置信号,用起来简单,只需要写个函数,跟内置信号绑定,当信号被触发,函数就会执行
# 如何用?
对于Django内置的信号,仅需注册指定信号,当程序执行相应操作时,自动触发注册函数
# 绑定信号 在django中有两种方式
@receiver
connect连接
# 自定义信号
就比内置信号多了两步:1 定义信号 2 触发信号 信号.send
Dockerfile用过吗?常用命令有哪些
# 什么是Dockerfile
Dockerfile是由一系列命令和参数构成的脚本文件,用来构建镜像
# FROM
设置要制作的镜像基于哪个镜像,FROM指令必须是整个Dockerfile的第一个指令,如果指定的镜像不存在默认会自动从Docker Hub上下载
# MAINTAINER
镜像作者的信息,比如名字或邮箱地址
# RUN
构建镜像时运行的shell命令
# CMD
CMD指令中指定的命令会在镜像运行时执行,在Dockerfile中只能存在一个,如果使用了多个CMD指令,则只有最后一个CMD指令有效
# EXPOSE
声明容器的服务端口
# ADD
将宿主机的文件复制到容器内,如果是一个压缩文件,将会在复制后自动解压
# COPY
拷贝文件或目录到镜像容器内,跟ADD类似,但不具备自动下载或解压功能
# ENV
设置容器的环境变量
标签:__,面试题,请求,对象,线程,服务器,客户端
From: https://www.cnblogs.com/super-xz/p/17327521.html