解释型和编译型语言的区别
编译型语言:把做好的源程序全部编译成二进制的可运行程序。然后,可直接运
行这个程序。如:C,C++
解释型语言:把做好的源程序翻译一句,然后执行一
句,直至结束!如:Python, (Java 有些特殊,java 程序也需要编译,但是没
有直接编译称为机器语言,而是编译称为字节码,然后用解释方式执行字节码。)
简述下 Python 中的字符串、列表、元组和字典
字符串(str):字符串是用引号括起来的任意文本,是编程语言中最常用的数
据类型。
列表(list):列表是有序的集合,可以向其中添加或删除元素。
元组(tuple):元组也是有序集合,但是是无法修改的。即元组是不可变的。
字典(dict):字典是无序的集合,是由 key-value 组成的。
集合(set):是一组 key的集合,每个元素都是唯一,不重复且无序的。
is 和 == 的区别
== 是比较操作符,只是判断对象的值(value)是否一致,而 is 则判断的是对象之间的身份(内存地址)是否一致。对象的身份,可以通过 id(),只有 id 一致时,is 比较才会返回 True,而当 value 一致时,== 比较就会返回 True
Python 的深浅拷贝
深浅拷贝,拷贝的是引用类型即:地址的指向
浅拷贝:浅拷贝只成功”独立“拷贝了列表的外层,而列表的内层列表,还是共享的
使用浅拷贝的时候,分为两种情况。
第一种,如果最外层的数据类型是可变的,比如说列表,字典等,浅拷贝会开启新的地址空间去存放。
第二种,如果最外层的数据类型是不可变的,比如元组,字符串等,浅拷贝对象的时候,还是引用对象的地址空间。
深拷贝:深拷贝使得两个列表完全独立开来,每一个列表的操作,都不会影响到另一个
深拷贝也分两种情况:
第一种,最外层数据类型可变。这个时候,内部和外部的都会拷贝过来。
第二种,外层数据类型不可变,如果里面是可变数据类型,会新开辟地址空间存放。如果内部数据类型不可变,才会如同浅拷贝一样,是对地址的引用。
可变类型与不可变类型
可变类型:允许变量的值发生变化,即如果对变量进行append、+=等这种操作后,只是改变了变量的值,而不会新建一个对象,变量引用的对象的地址也不会变化,不过对于相同的值的不同对象,在内存中则会存在不同的对象,即每个对象都有自己的地址,相当于内存中对于同值的对象保存了多份,这里不存在引用计数,是实实在在的对象。(list,dict)
不可变类型:python中的不可变数据类型,不允许变量的值发生变化,如果改变了变量的值,相当于是新建了一个对象,而对于相同的值的对象,在内存中则只有一个对象,内部会有一个引用计数来记录有多少个变量引用这个对象;(int,float,string,tuple)
单例模式与工厂模式
单例模式:主要目的是确保某一个类只有一个实例存在
工厂模式:包涵一个超类,这个超类提供一个抽象化的接口来创建一个特定类型
的对象,而不是决定哪个对象可以被创建
Python 中的 GIL
GIL 是 Python 的全局解释器锁,同一进程中假如有多个线程运行,一个线程在运行 Python 程序的时候会占用 Python 解释器(加了一把锁即 GIL),使该进程内的其他线程无法运行,等该线程运行完后其他线程才能运行。如果线程运行过程中遇到耗时操作,则解释器锁解开,使其他线程运行。所以在多线程中,线程的运行仍是有先后顺序的,并不是同时进行
简述闭包
闭包
简述装饰器
装饰器
python的内存管理
以下三方面:
垃圾回收机制:
a. 引用计数:也是一种垃圾回收机制,而且也是一种最直观,最简单的垃圾收集技术。
当python某个对象的引用计数降为 0 时,说明没有任何引用指向该对象,该对象就成为要被回收的垃圾了。
优点:简单实时性。
缺点:维护引用计数消耗资源,且无法解决循环引用
b. 标记清除:创建特殊链表专门用于保存 列表、元组、字典、集合、自定义类等对象,之后再去检查这个链表中的对象是否存在循环引用,如果存在则让双方的引用计数器均 - 1 。
缺点:清除非活动的对象前它必须顺序扫描整个堆内存,哪怕只剩下小部分活动对象也要扫描所有对象。
c. 分代回收: 对标记清除中的链表进行优化,将那些可能存在循引用的对象拆分到3个链表,链表称为:1/2/3三代,每代都可以存储对象和阈值,当达到阈值时,就会对相应的链表中的每个对象做一次扫描,除循环引用各自减1并且销毁引用计数器为0的对象。
引用计数: 在refchain(环状双向链表)中的所有对象内部都有一个ob_refcnt用来保存当前对象的引用计数器,顾名思义就是自己被引用的次数。当对象被引用的时候,不会在内存中重复创建数据,而是引用计数器+1 。当对象被销毁的时候,会让引用计数器-1。如果引用计数器为0,则将对象从refchain链表中摘除,同时垃圾回收机制会将此对象占用的内存收回。
缓存机制:为了进一步提升效率,避免频繁地进行对象的申请和销毁,Python内部维护了一个池和一个free_list数组。池的做法是预先缓存一些常见的对象,比如-5到256的整型对象,和ascii字符等。当程序中定义一个小的整型对象时,不需要创建,直接从池中获取就行。free_list的做法是将程序本来删除的对象,没有直接销毁而是放在一个名为free_list的数组中。后面当程序中重新创建一个相同类型的对象时,不必新开辟空间而是直接从free_list中获取对象,重新进行初始化并放入refchain中去
python迭代器和生成器
迭代器:是一个类,需要满足魔法方法__iter__和__next__
生成器:是由yield函数实现的一个函数,遇到yield后会先暂停,返回yield后面的结果,然后挂起,直到下次执行的时候,再从挂起点开始运行。
简述 OSI 七层协议
应用层:网络服务与最终用户的一个接口。
协议有:HTTP FTP TFTP SMTP SNMP DNS TELNET HTTPS POP3 DHCP
表示层:数据的表示、安全、压缩。(在五层模型里面已经合并到了应用层)格式有JPEG、ASCll、DECOIC、加密格式等
会话层:建立、管理、终止会话。(在五层模型里面已经合并到了应用层)对应主机进程,指本地主机与远程主机正在进行的会话
传输层:定义传输数据的协议端口号,以及流控和差错校验。
协议有:TCP UDP,数据包一旦离开网卡即进入网络传输层
网络层:进行逻辑地址寻址,实现不同网络之间的路径选择。
协议有:ICMP IGMP IP(IPV4 IPV6)
数据链路层:建立逻辑连接、进行硬件地址寻址、差错校验 [2] 等功能。(由底层网络定义协议)将比特组合成字节进而组合成帧,用MAC地址访问介质,错误发现但不能纠正。
物理层:建立、维护、断开物理连接。(由底层网络定义协议)
三次握手、四次挥手的流程
三次握手(Three-way Handshake)其实就是指建立一个TCP连接时,需要客户端和服务器总共发送3个包。进行三次握手的主要作用就是为了确认双方的接收能力和发送能力是否正常、指定自己的初始化序列号为后面的可靠性传送做准备。实质上其实就是连接服务器指定端口,建立TCP连接,并同步连接双方的序列号和确认号,交换TCP窗口大小信息。
刚开始客户端处于 Closed 的状态,服务端处于 Listen 状态。
进行三次握手:
第一次握手: 客户端给服务端发一个 SYN 报文,并指明客户端的初始化序列号 ISN。此时客户端处于 SYN_SENT 状态。
首部的同步位SYN=1,初始序号seq=x,SYN=1的报文段不能携带数据,但要消耗掉一个序号。
第二次握手: 服务器收到客户端的 SYN 报文之后,会以自己的 SYN 报文作为应答,并且也是指定了自己的初始化序列号 ISN(s)。同时会把客户端的 ISN + 1 作为ACK 的值,表示自己已经收到了客户端的 SYN,此时服务器处于 SYN_RCVD 的状态。
在确认报文段中SYN=1,ACK=1,确认号ack=x+1,初始序号seq=y。
第三次握手: 客户端收到 SYN 报文之后,会发送一个 ACK 报文,当然,也是一样把服务器的 ISN + 1 作为 ACK 的值,表示已经收到了服务端的 SYN 报文,此时客户端处于 ESTABLISHED 状态。服务器收到 ACK 报文之后,也处于 ESTABLISHED 状态,此时,双方已建立起了连接。
确认报文段ACK=1,确认号ack=y+1,序号seq=x+1(初始为seq=x,第二个报文段所以要+1),ACK报文段可以携带数据,不携带数据则不消耗序号。
发送第一个SYN的一端将执行主动打开(active open),接收这个SYN并发回下一个SYN的另一端执行被动打开(passive open)。
在socket编程中,客户端执行connect()时,将触发三次握手。
- 四次挥手
建立一个连接需要三次握手,而终止一个连接要经过四次挥手(也有将四次挥手叫做四次握手的)。这由TCP的半关闭(half-close)造成的。所谓的半关闭,其实就是TCP提供了连接的一端在结束它的发送后还能接收来自另一端数据的能力。
TCP 连接的拆除需要发送四个包,因此称为四次挥手(Four-way handshake),客户端或服务端均可主动发起挥手动作。
刚开始双方都处于ESTABLISHED 状态,假如是客户端先发起关闭请求。四次挥手的过程如下:
第一次挥手: 客户端发送一个 FIN 报文,报文中会指定一个序列号。此时客户端处于 FIN_WAIT1 状态。
即发出连接释放报文段(FIN=1,序号seq=u),并停止再发送数据,主动关闭TCP连接,进入FIN_WAIT1(终止等待1)状态,等待服务端的确认。
第二次挥手: 服务端收到 FIN 之后,会发送 ACK 报文,且把客户端的序列号值 +1 作为 ACK 报文的序列号值,表明已经收到客户端的报文了,此时服务端处于 CLOSE_WAIT 状态。
即服务端收到连接释放报文段后即发出确认报文段(ACK=1,确认号ack=u+1,序号seq=v),服务端进入CLOSE_WAIT(关闭等待)状态,此时的TCP处于半关闭状态,客户端到服务端的连接释放。客户端收到服务端的确认后,进入FIN_WAIT2(终止等待2)状态,等待服务端发出的连接释放报文段。
第三次挥手: 如果服务端也想断开连接了,和客户端的第一次挥手一样,发给 FIN 报文,且指定一个序列号。此时服务端处于 LAST_ACK 的状态。
即服务端没有要向客户端发出的数据,服务端发出连接释放报文段(FIN=1,ACK=1,序号seq=w,确认号ack=u+1),服务端进入LAST_ACK(最后确认)状态,等待客户端的确认。
第四次挥手: 客户端收到 FIN 之后,一样发送一个 ACK 报文作为应答,且把服务端的序列号值 +1 作为自己 ACK 报文的序列号值,此时客户端处于 TIME_WAIT 状态。需要过一阵子以确保服务端收到自己的 ACK 报文之后才会进入 CLOSED 状态,服务端收到 ACK 报文之后,就处于关闭连接了,处于 CLOSED 状态。
即客户端收到服务端的连接释放报文段后,对此发出确认报文段(ACK=1,seq=u+1,ack=w+1),客户端进入TIME_WAIT(时间等待)状态。此时TCP未释放掉,需要经过时间等待计时器设置的时间2MSL后,客户端才进入CLOSED状态。
收到一个FIN只意味着在这一方向上没有数据流动。客户端执行主动关闭并进入TIME_WAIT是正常的,服务端通常执行被动关闭,不会进入TIME_WAIT状态。
在socket编程中,任何一方执行close()操作即可产生挥手操作。
TCP 和 UDP 的区别
1、 TCP面向连接 (如打电话要先拨号建立连接); UDP是无连接 的,即发送数据之前不需要建立连接
2、TCP提供可靠的服务。也就是说,通过TCP连接传送的数据,无差错,不丢失,不重复,且按序到达;UDP尽最大努力交付,即不保证可靠交付
Tcp通过校验和,重传控制,序号标识,滑动窗口、确认应答实现可靠传输。如丢包时的重发控制,还可以对次序乱掉的分包进行顺序控制。
3、UDP具有较好的实时性,工作效率比TCP高,适用于对高速传输和实时性有较高的通信或广播通信。
4.每一条TCP连接只能是点到点的;UDP支持一对一,一对多,多对一和多对多的交互通信
5、TCP对系统资源要求较多,UDP对系统资源要求较少。
什么是 socket?简述基于 TCP协议的套接字通信流程
socket 是对 TCP/IP 协议的封装,它的出现只是使得程序员更方便地使用TCP/IP 协议栈而已。socket 本身并不是协议,它是应用层与 TCP/IP 协议族通信的中间软件抽象层,是一组调用接口(TCP/IP 网络的 API 函数)
“TCP/IP 只是一个协议栈,就像操作系统的运行机制一样,必须要具体实现,同时还要提供对外的操作接口。 这个就像操作系统会提供标准的编程接口,比如win32 编程接口一样。TCP/IP 也要提供可供程序员做网络开发所用的接口,这就是 Socket 编程接口。”
简述 进程、线程、协程的区别以及应用场景
进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位。每个进程都有自己的独立内存空间,不同进程通过进程间通信来通信
线程是进程的一个实体,是 CPU 调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源
协程是一种用户态的轻量级线程,协程的调度完全由用户控制。协程拥有自己的寄存器上下文和栈
HTTP和HTTPS
http协议和https协议的区别主要是:传输信息安全性不同、连接方式不同、端口不同、证书申请方式不同
一、传输信息安全性不同
http协议:是超文本传输协议,信息是明文传输。如果攻击者截取了Web浏览器和网站服务器之间的传输报文,就可以直接读懂其中的信息。
https协议:是具有安全性的ssl加密传输协议,为浏览器和服务器之间的通信加密,确保数据传输的安全。
二、连接方式不同
http协议:http的连接很简单,是无状态的。
https协议:是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议。
三、端口不同
http协议:使用的端口是80。
https协议:使用的端口是443。
四、证书申请方式不同
http协议:免费申请。
https协议:需要到ca申请证书,一般免费证书很少,需要交费
HTTP 常见请求方式
GET,POST,PUT,DELETE,PATCH
HTTP 状态码
1xx: 信息
2xx:成功
3xx:重定向
4xx:客户端错误
5xx:服务器错误
什么是数据库事务
事务(Transaction)是并发控制的基本单位。所谓的事务,它是一个操作序列,
这些操作要么都执行,要么都不执行,它是一个不可分割的工作单位
在关系数据库中,一个事务可以是一条 SQL 语句、一组 SQL 语句或整个程序。
四个属性:原子性,一致性,隔离性和持久性
SQL优化
SQL优化方式
MySQL 索引种类
普通索引, 唯一索引, 主键索引, 组合索引, 全文索引
无法命中索引的情况
使用 or 关键字会导致无法命中索引
左前导查询会导致无法命中索引,如 like ‘%a’ 或者 like ‘%a%’ 单列索引的索引列为 null 时全值匹配会使索引失效,组合索引全为 null 时索引失效组合索引不符合左前缀原则的列无法命中索引,如我们有 4 个列 a、b、c、d,我们创建一个组合索引 INDEX(a,b,c,d),那么能命中索引的查询为 a,ab,abc,abcd,除此之外都无法命中索引
强制类型转换会导致索引失效
负向查询条件会导致无法使用索引,比如 NOT IN,NOT LIKE,!= 等 如果 mysql
估计使用全表扫描要比使用索引快,则不使用索引
redis 有哪几种持久化策略
RDB 持久化:是将 Reids 在内存中的数据库记录定时 dump 到磁盘上的持久化
AOF(append only file)持久化:将 Reids 的操作日志以追加的方式写入文件
redis 支持的过期策略
通用的三种过期策略
定时删除: 在设置 key 的过期时间的同时,为该 key 创建一个定时器,让定时器在 key 的过期时间来临时,对 key 进行删除
惰性删除: key 过期的时候不删除,每次从数据库获取 key 的时候去检查是否过期,若过期,则删除,返回 null
定期删除: 每隔一段时间执行一次删除过期 key 操作
redis 采用惰性删除+定期删除策略
如何高效的找到 redis 中的某个 KEY
import redis
con = redis.Redis()
con.keys(pattern='key*') # *代表通配符
基于 redis 实现先进先出、后进先出及优先级队列
先进先出(队列):使用redis的list数据类型,右添加rpush,左删除lpop
后进先出(栈):使用redis的list数据类型,有添加rpush,右删除rpop
优先级队列:使用redis的zset有序集合,zadd有序添加,zrange获取有序范围的数据,zrem有序删除
redis 分布式锁
为 redis 集群设计的锁,防止多个任务同时修改数据库,其本质就是为集群中的每个主机设置一个会超时的字符串,当集群中有一半多的机器设置成功后就认为加锁成功,直至锁过期或解锁不会有第二个任务加锁成功
简述 MVC 和 MTV
所谓 MVC 就是把 web 应用分为模型(M),控制器©,视图(V)三层,他们之间以一种插件似的,松耦合的方式连接在一起。模型负责业务对象与数据库的对象(ORM),视图负责与用户的交互(页面),控制器©接受用户的输入调用模型和视
图完成用户的请求
Django 中的 MTV 模式:
Model(模型):负责业务对象与数据库的对象(ORM)
Template(模版):负责如何把页面展示给用户
View(视图):负责业务逻辑,并在适当的时候调用 Model 和 Template,本质上与 MVC 相同
什么是 ORM
ORM 的全称是 Object Relational Mapping,即对象关系映射。它的实现思想就是将关系数据库中表的数据映射成为对象,以对象的形式展现,这样开发人员就可以把对数据库的操作转化为对这些对象的操作
使用 ORM 和原生 SQL 的优缺点
优点:
cookie 和 session 的区别
cookie 是保存在浏览器端的键值对,可以用来做用户认证
sesseion 是将用户的会话信息保存在服务端,key 值是随机产生的字符串,value值是 session 的内容,依赖于 cookie 将每个用户的随机字符串保存到用户浏览器中
列举一些 django 的内置组件
Admin 组件:是对 model 中对应的数据表进行增删改查提供的组件
model 组件:负责操作数据库
form 组件:生成 HTML 代码;数据有效性校验;校验信息返回并展示
ModelForm 组件:用于数据库操作,也可用于用户请求的验证