Python面试题

面试题准备

001.问题:说明os,sys模块不同,并列举常用的模块方法?

参考答案:os(operation system 操作系统):  
提供一种方便的使用操作系统函数的方法sys(system):  
提供访问由解释器使用或维护的变量和在与解释器交互使用到的函数。
os模块常用的方法:    
	1.os.listdir() 列出目录下的文件
    2.os.remove() 删除文件
    3.os.rename() 重命名文件
    4.os.chdir() 改变路径
    5.os.mkdir() 创建文件夹    注意点:递归遍历某个文件夹下的所有文件    【扩展】:面试过程中肯会被问题:如果给你一个文件夹这个文件中也嵌套N层文件夹,且每层文件夹中是否有普通的文件 不一定        
 请写一个程序,将所有的不同层级的文件夹中的文件名 进行打印sys模块常用的方法:    
 1.sys.path 模块的搜索路径(我们的python程序遇到了import时,到底去哪里找这些模块,sys.path指定的路径中找)    2.sys.stdin 就是标准输入,与此类推sys.stdout就是标准输出        只要是程序 一般会有3个名称(标准输入stdin、标准输出stdout、标准错误stderror)"""import sysprint("-----1----")print(sys.stdin)print("-----2----")print(sys.stdin.read(3))  # 从键盘获取2个数据print("-----3----")sys.stdout.write("111111")  # 项目屏幕输出111111print("-----4----")

002.问题:print 调用 Python 中底层的什么方法?

参考答案:print 方法默认调用 sys.stdout.write 方法,即往控制台打印字符串。
import sysprint("hello")  # 输出
hellosys.stdout.write("hello")  # 输出
hellosys.stdout.write("\n")  # 输出hello

003.问题:os.path和sys.path的区别?

参考答案:os.path 是一个模块主要用于获取文件的属性。例如 os.path.basename(path)返回文件名sys.path是由目录名构成的列表,Python 从中查找扩展模块( Python 源模块, 编译模块,或者二进制扩展).启动Python 时,这个列表从根据内建规则,PYTHONPATH 环境变量的内容, 以及注册表( Windows 系统)等进行初始化import os
import sys

print(os.path)
print(sys.path)

004.问题:4G 内存怎么读取一个 5G 的数据?

参考答案:# 方法一可以通过生成器,分多次读取,每次读取数量相对少的数据(比如 500MB)进行处理,处理结束后再读取后面的 500MB 的数据。# 方法二可以通过 linux 命令 split 切割成小文件,然后再对数据进行处理,此方法效率比较高。可以按照行数切割,可以按照文件大小切割。

005.问题1:假如有一个的文件 test.txt 大小约为 10K,现在内存只有 4G 无法一次性读入 10G 的文件,需要分批读入

#假如有一个的文件 test.txt 大小约为 10K,之前处理文件的,代码如下所示:
def get_lines():
    l = []
    with open('test.txt', 'rb') as f:
        for eachline in f:
            l.append(eachline)
    return l


def process(temp_data):
    print(temp_data)  # 用打印来模拟处理过程


if __name__ == '__main__':
    for e in get_lines():
        process(e)  # 处理每一行数据
#现在要处理一个大小为 10G 的文件,但是内存只有 4G,如果在只修改 get_lines 函数而其他代 码保持不变的情况下,应该如何实现?需要考虑的问题都有哪些?
# 参考答案:
def get_lines():
    with open('test.txt', 'rb') as f:
        while True:
            data = f.read(1024 * 1024)  # 每次读取1M大小内容
            if data:
                yield data
            else:
                return


"""
需要考虑的问题:
内存只有 4G 无法一次性读入 10G 的文件,需要分批读入。
分批读入数据要记录每次读入数据的位置。分批每次读入数据的大小,太小就会在读取操作上花费过多时间。
"""

006.递归次数过多,出现异常

import os


def print_directory_contents(temp_path):
    """
    这个函数接收文件夹的名称作为输入参数
    返回该文件夹中文件的路径
    以及其包含文件夹中文件的路径
    """
    # 补充代码

    for temp in os.listdir(temp_path):
        temp_child_path = os.path.join(temp_path, temp)
        if os.path.isdir(temp_child_path):  # 判断是否是一个文件夹,如果是返回True,否则False
            print_directory_contents(temp_child_path)
        else:
            print(temp_child_path)


print_directory_contents("./")


"""
提示:上述的代码会出bug。
bug:如果文件夹嵌套层数太多,会导致递归次数过多,出现异常
debug:可以考虑 os.walk方式实现

【思考】
请使用os.walk方式冲洗实现上述要求
"""
def print_walk(temp_path):
    """
    这个函数接收文件夹的名称作为输入参数
    返回该文件夹中文件的路径
    以及其包含文件夹中文件的路径
    """
    # 补充代码

    for paths, temp, name in os.walk(temp_path):
        # print(temp, "*" * 5, paths, "*" * 5, name)
        for i in name:
            print(os.path.join(paths, i))


print_walk("./")

007.在except中return后还会不会执行finally中的代码? 怎么抛出自定义异常?

"""参考答案:
会继续处理 finally 中的代码;用 raise 方法可以抛出自定义异常。
"""
def test():
    try:
        1 / 0
    except Exception as ret:
        print("出现异常....")
        print(ret)
        return 0
    else:
        print("未出现异常....")
        return 1
    finally:
        print("finally执行了")
        return 2
ret = test()
print(ret)

008.常用的 Python 标准库都有哪些?

"""
参考答案:
"""
# 常用库
# os 操作系统
# time 时间
# random 随机
# threading 线程
# multiprocessing进程
# queue 队列

# 第三方库:
# django 和 flask 也是第三方库,requests,virtualenv,selenium,scrapy,celery,
# re,hashlib,md5

009.init 和__new__的区别?

"""参考答案:
__init__ 在对象创建后,对对象进行初始化。
__new__ 是在对象创建之前创建一个对象,并将该对象返回给 init。
【联想】
到一个关联点:单例
"""

010.打乱一个排好序的 list 怎样实现?

"""
参考答案如下:
"""
import random

nums = [x for x in range(1, 11)]

print("操作之前nums=", nums)

random.shuffle(nums)  # shuffle单词意思是:洗牌

print("操作之后nums=", nums)

011.Python 是强语言类型还是弱语言类型?

"""
参考答案:
Python 是强类型的动态脚本语言。
强类型:不允许不同类型相加。
动态:不使用显示数据类型声明,且确定一个变量的类型是在第一次给它赋值的时候。
脚本语言:一般也是解释型语言,运行代码只需要一个解释器,不需要编译。
"""

print("1" + 1)

# 在C语言、C++中,定义一个变量的时候 一定要什么其数据类型
# int a = 100;
# int a;
# 在python中,可以先定义变量,当真正需要变量的数据的时候再确定其类型
# a = None
# a = 100

012.什么是Python的自省?

"""
什么是自省?

在日常生活中,自省(introspection)是一种自我检查行为。
在计算机编程中,自省是指这种能力:检查某些事物以确定它是什么、它知道什么以及它能做什么。自省向程序员提供了极大的灵活性和控制力。
说的更简单直白一点:自省就是面向对象的语言所写的程序在运行时,能够知道对象的类型。简单一句就是,运行时能够获知对象的类型。

例如:python, ruby, object-C, c++都有自省的能力,这里面的c++的自省的能力最弱,只能够知道是什么类型,而像python可以知道是什么类型,还有什么属性。

Python中比较常见的自省(introspection)机制(函数用法)有: 
dir(),type(), hasattr(), isinstance()
通过这些函数,我们能够在程序运行时得知对象的类型,判断对象是否存在某个属性,访问对象的属性。
"""

# 1. dir() 函数可能是 Python 自省机制中最著名的部分了。
# 它返回传递给它的任何对象的属性名称经过排序的列表。
names = ["张三", "李四"]
print(dir(names))

# 2. type() 函数有助于我们确定对象是字符串还是整数,或是其它类型的对象。
print(type(100))
print(type("100"))

# 3. Python中对象拥有属性,并且 dir() 函数会返回这些属性的列表。
# 但是,有时我们只想测试一个或多个属性是否存在。这个任务可以由 hasattr() 和 getattr() 函数来完成.
print(hasattr(names, '__doc__'))  # True
print(hasattr(names, 'append'))  # True
append = getattr(names, 'append')  # 之前我们在实现mini_web框架的时候 使用过了,如果想了解一下 可以到mini_web框架课程中复习
append("老王")
print(names)

# 4.  isinstance() 可以测试对象,以确定它是否是某个特定类型或定制类的实例:
print(isinstance("python", str))

013.Python所遵循的代码规范是什么?请举例说明其要求?

"""参考答案:
PEP8 规范
1.常量:大写加下划线 USER_CONSTANT。
2.私有变量 : 前导2个下划线 __value。
Python 中不存在私有变量一说,若是遇到需要保护的变量,使用小写和一个前导下划线。但这只是
程序员之间的一个约定,用于警告说明这是一个私有变量,外部类不要去访问它。但实际上,外部类还
是可以访问到这个变量。
3.内置变量 : 小写,两个前导下划线和两个后置下划线 __class__
两个前导下划线会导致变量在解释期间被更名。这是为了避免内置变量和其他变量产生冲突。用户
定义的变量要严格避免这种风格。以免导致混乱。
4.函数名:小写+下划线
5.类名:大驼峰
。。。
"""

014.现有字典 d={‘a’: 24, ‘g’: 52, ‘l’: 12, ‘k’: 33} 请按字典中的 value 值进行排序?

# 参考答案如下:
d = {
    'a': 24,
    'g': 52,
    'l': 12,
    'k': 33
}
d_order_list = sorted(d.items(), key=lambda x: x[1])
print(d_order_list)
print(dict(d_order_list))
d_order_list = sorted(d.items(), key=lambda x: x[1], reverse=True)  # reverse=True的作用是将sorted函数由从小到到排序,变为从大到小排序
print(d_order_list)
print(dict(d_order_list))

015.给定两个列表,怎么找出他们相同的元素和不同的元素?

# 参考答案
list1 = [1, 2, 3]
list2 = [3, 4, 5]
set1 = set(list1)
set2 = set(list2)
print(set1 & set2)  # & 的作用是取交集
print(set1 ^ set2)  # ^ 的作用是获取只在一个集合中出现的元素
print(set1 | set2)  # | 的作用是并集

016.下面这段代码的输出结果是什么?请解释?

def extend_list(val, l=[]):
    l.append(val)
    return l


list1 = extend_list(10)
list2 = extend_list(123, [])
list3 = extend_list('a')

print("list1 = %s" % list1)
print("list2 = %s" % list2)
print("list3 = %s" % list3)


"""
# 输出结果
list1 = [10, 'a']
list2 = [123]
list3 = [10, 'a']

# 说明
新的默认列表只在函数被定义的那一刻创建一次,而不是在调用的时候被计算。
"""

017.内存泄漏是什么?如何避免?

"""参考答案:

创建一个对象时候,就相当于申请了一块内存
当程序结束的时候肯定会自动释放那块内存

但是 有很多时候 程序需要运行很久,例如 交换机中的程序(或者自己家里的路由器),如果在程序运行结束之前 某些对象已经不用了
但是忘记了释放这个块内存,那么久而久之 程序就会越来越卡,效率会越来越低
这就是 内存泄漏带来的问题

最典型的案例:对象之间互相引用

指由于疏忽或错误造成程序未能释放已经不再使用的内存的情况。
内存泄漏并非指内存在物理上的消失,而是应用程序分配某段内存后,由于设计错误,失去了对该段内存的控制,因而造成了内存的浪费。
导致程序运行速度减慢甚至系统崩溃等严重后果。

对象间的循环引用是导致内存泄漏的主凶。

通过 Python 扩展模块 gc 来查看不能回收的对象的详细信息。
可以通过 sys.getrefcount(obj) 来获取对象的引用计数,并根据返回值是否为 0 来判断是否内存泄漏。

有三种情况会触发垃圾回收:
当gc模块的计数器达到阈值的时候,自动回收垃圾
调用gc.collect(),手动回收垃圾
程序退出的时候,python解释器来回收垃圾

垃圾回收问题是面试常备问到的问题,详细GC(垃圾回收)可以参考如下地址
http://www.codetutor.top/docs/01-learn/01-python/02-advanced/08-%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6%E3%80%81log%E6%97%A5%E5%BF%97/01-%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6%EF%BC%88%E4%B8%80%EF%BC%89.html
"""

018.Python 中 is 和==的区别?

"""参考答案:
is 判断的是 a 对象是否就是 b 对象,是通过 id 来判断的。
== 判断的是 a 对象的值是否和 b 对象的值相等,是通过 value 来判断的。
"""

a = ['abc']
b = ['abc']

print(a is b)
print(a == b)

019.谈谈你对多进程,多线程,以及协程的理解,项目是否用

"""
参考答案:
进程:一个运行的程序(代码)就是一个进程,没有运行的代码叫程序,进程是系统资源分配的最小单位,进程拥有自己独立的内存空间,所以进程间数据不共享,开销大。

线程: 调度执行的最小单位,也叫执行路径,不能独立存在,依赖进程存在一个进程至少有一个线程,叫主线程,而多个线程共享内存(数据共享,共享全局变量),从而极大地提高了程序的运行效率。

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

下载文件时,用过多线程技术,或者业余时间用过多线程写爬虫,提升效率。


"""

020.什么是多线程竞争?

"""
参考答案:
线程是非独立的,同一个进程里线程是数据共享的,当各个线程访问数据资源时会出现竞争状态
即:数据几乎同步会被多个线程占用,造成数据混乱 ,即所谓的线程不安全
锁的好处:确保了某段关键代码(共享数据资源)只能由一个线程从头到尾完整地执行能解决多线程资源竞争下的原子操作问题。
锁的坏处:阻止了多线程并发执行,包含锁的某段代码实际上只能以单线程模式执行,效率就大大地下降了

锁的致命问题:死锁。
"""

020.什么是死锁呢?怎样避免?

"""参考答案:
若干子线程在系统资源竞争时,都在等待对方对某部分资源解除占用状态,结果是谁也不愿先解锁,互相干等着,程序无法执行下去,这就是死锁。

可以通过2种方式来避免
1. 添加超时时间
2. 银行家算法

"""

import threading
import time


def test1():
    mA.acquire()
    print("test1--上锁A")
    time.sleep(1)
    mB.acquire(timeout=3)  # 进行上锁的最长时间为3秒钟,如果在这期间这个锁被解锁了,那么就会上锁成功,从而解堵塞,如果超时时间到了那么程序上锁失败,但程序依然向下运行
    print("test1---上锁B")
    mA.release()
    print("test1---解锁A")
    # mB.release()
    print("test1---解锁B")


def test2():
    mB.acquire()
    print("test2---上锁B")
    time.sleep(1)
    mA.acquire()
    print("test2--上锁A")
    mB.release()
    print("test2---解锁B")
    mA.release()
    print("test2---解锁A")


mA = threading.Lock()
mB = threading.Lock()


def main():
    t1 = threading.Thread(target=test1)
    t2 = threading.Thread(target=test2)
    t1.start()
    t2.start()


if __name__ == '__main__':
    main()

"""
用下面的test1函数替代上面的test1,即可实现通过添加超时时间避免死锁问题
"""


def test1():
    mA.acquire()
    print("test1--上锁A")
    time.sleep(1)
    mB.acquire(timeout=3)  # 在此处添加了超时时间,此处上锁超过3秒自动解锁
    print("test1---上锁B")
    mA.release()
    print("test1---解锁A")
    # mB.release()  因为是死锁,所有在上面代码一定会达到超时时间自动解锁,执行到此处时会报错(release unlocked lock)无法解锁已经解开的锁,要注释掉
    print("test1---解锁B")

021说说下面几个概念 同步,异步,阻塞,非阻塞?

"""
参考答案:
同步:多个任务之间有先后顺序执行,一个执行完下个才能执行。

异步:多个任务之间没有先后顺序,可以同时执行有时候一个任务可能要在必要的时候获取另一个同时执行的任务的结果,这个就叫回调!

阻塞:如果卡住了调用者,调用者不能继续往下执行,就是说调用者阻塞了。

非阻塞:如果不会卡住,可以继续执行,就是说非阻塞的。

同步异步相对于多任务而言
阻塞非阻塞相对于代码执行而言


"""

# 下面是异步的demo

import os
import random
import time
from multiprocessing import Pool
from urllib import request


def get_page(url):
    print('<进程%s> get %s' % (os.getpid(), url))
    respone = request.urlopen(url)
    if respone.getcode() == 200:
        time.sleep(random.randint(1, 20))
        return {'url': url, 'text': respone.read()}


def pasrse_page(res):
    print('<进程%s> parse %s' % (os.getpid(), res['url']))
    parse_res = 'url:<%s> size:[%s]\n' % (res['url'], len(res['text']))
    with open('db.txt', 'a') as f:
        f.write(parse_res)


if __name__ == '__main__':
    urls = [
        'https://www.baidu.com',
        'http://www.codetutor.top',
        'https://www.cnblogs.com/'
    ]

    print("主进程-->pid=%s" % os.getpid())

    p = Pool()

    print("主进程-->创建多个子进程")
    for url in urls:
        res = p.apply_async(get_page, args=(url,), callback=pasrse_page)

    print("主进程-->创建子进程完毕")

    for i in range(100):
        print("主进程-->模拟处理事情...")
        time.sleep(0.2)

    p.close()
    p.join()
    print("主进程-->介绍")

022什么是僵尸进程和孤儿进程?怎么避免僵尸进程?

"""
参考答案:

僵尸进程:
任何一个子进程(init除外)在exit()之后,并非马上就消失掉,而是留下一个称为僵尸进程(Zombie)的数据结构,等待父进程处理。

为什么要处理:
如果僵尸进程不被父进程处理的话,那么保留的那段信息就不会释放,其进程号就会一直被占用,但是系统所能使用的进程号是有限的,
如果大量的产生僵死(僵尸)进程,将因为没有可用的进程号而导致系统不能产生新的进程.
此即为僵尸进程的危害,应当避免。

怎么处理:
创建进程有多种方式,在Linux系统下操作系统提供的api为fork,所以当我们再使用fork创建子进程后,父进程在运行结束之前需要调用wait()或者waitpid()
它们的作用就是回收僵尸进程的数据结构的,从而把之前占用的进程号等重新返回给操作系统以便以后重新分配给新的进程使用


万一父进程在调用wait()、waitpiad()之前已经退出了,那么谁来负责回收子进程呢?
如果父进程在子进程结束之前退出,则子进程将由init进程接管。init进程将会以父进程的身份对僵尸状态的子进程进行处理。

孤儿进程:
父进程已经结束而子进程还在运行,此子进程就是孤儿进程


os.getppid()  # 获取父进程的pid
os.getpid()  # 获取当前进程的pid
"""

import multiprocessing

xxxx = multiprocessing.Process()
xxxx.start()
xxxx.join()  # 会等待子进程,其实说实话,就是在等待子进程死掉,然后回收它的内存空间

023-1.Python中的进程与线程的使用场景?

参考答案:
多进程适合在 计算密集型操作(cpu 操作指令比较多,如位数多的浮点运算)。
多线程适合在 IO 密集型操作(读写数据操作较多的,比如爬虫)

023-2.IO 密集型和 计算 密集型区别?

参考答案:
IO 密集型:系统运作,大部分的状况是 CPU 在等 I/O (硬盘/内存)的读/写。
计算密集型:大部份时间用来做计算、逻辑判断等 CPU 动作的程序称之 计算密集型。

024.谈一谈TCP与UDP

参考答案:
1. TCP与UDP都是TCP/IP协议(簇)中的一种网络协议,用来规定数据在网络传输过程中的方式
2. 从稳定角度看,TCP要比UDP好,因为:TCP每次发送完数据后,接收方会会送一个ACK的数据 以便发送方验证接收方数据已经收到;
    而UDP发送完毕后没有这样的验证
3. TCP有3次握手、4次挥手;UDP没有
4. 像今天浏览器上网用到的HTTP(s)协议底层都是采用TCP进行封装数据

025.请简单说一下三次握手和四次挥手?

参考答案:

三次握手过程:
1 首先客户端向服务端发送一个带有 SYN 标志,以及随机生成的序号(例如 1)的数据包
2 服务端收到数据包后返回一个数据包(SYN为随机数(例如100),ACK为2)给客户端
3 客户端再次发送带有 ACK 标志101序号的数据包给服务端至此三次握手过程结束,客户端开始向服务端发送数据。

客户端                     服务器
-------------SYN(1)-------->
<--------SYN(100) ACK(2)----
-------------ACK(101)------>

以生活中的案例对比三次握手
1. 客户端向服务端发起请求:我想给你通信,你准备好了么?
2. 服务端收到请求后回应客户端:I'ok,你准备好了么
3. 客户端礼貌的再次回一下客户端:准备就绪,咱们开始通信吧!
整个过程跟打电话的过程一模一样:
1 喂,你在吗
2 在,我说的你听得到不
3 恩,听得到(接下来请开始你的表演)
补充:SYN:请求询问,ACk:回复,回应。

四次挥手过程:
由于 TCP 连接是可以双向通信的(全双工),因此每个方向都必须单独进行关闭(这句话才是精辟,后面四个挥手过程都是其具体实现的语言描述)
四次挥手过程,客户端和服务端都可以先开始断开连接
1. 客户端发送带有 FIN 标识的报文给服务端,请求通信关闭
2. 服务端收到信息后,回复 ACK 答应关闭客户端通信(连接)请求
3. 服务端发送带有 FIN 标识的报文给客户端,也请求关闭通信
4. 客户端回应 ACK 给服务端,答应关闭服务端的通信(连接)请求

    客户端                                    服务器
准备要关闭发送  --------------FIN---------->   准备要关闭接收
关闭发送成功   <--------------ACK-----------   关闭接收成功
准备要关闭接收 <--------------FIN-----------   准备要关闭发送
关闭接收成功   ---------------ACK---------->   关闭发送成功

026说一下什么是 TCP 的 2MSL?

参考答案:
主动发送 FIN 关闭的一方,在 4 次挥手最后一次要等待一段时间我们称这段时间为 2MSL(此时的状态为TIME_WAIT)

2MSL存在有两个理由:
1.4 次挥手关闭流程更加可靠
2.防止丢包后对后续新建的正常连接的传输造成破坏

027.描述用浏览器访问 www.baidu.com 的过程

参考答案:
1. 先要解析出 www.baidu.com 对应的 ip 地址
2. 要先使用 ARP 网络协议 获取默认网关的 mac 地址
3. 组织数据发送给默认网关(ip 还是 dns 服务器的 ip,但是 mac 地址是默认网关的 mac 地址) 默认网关拥有转发数据的能力
4. 数据被转发到 DNS 服务器
5. DNS 服务器查询解析出 www.baidu.com 对应的 ip 地址,并原路返回请求这个域名的客户端
6. 得到了 www.baidu.com 对应的 ip 地址之后,会发送 TCP 的 3 次握手,进行连接web服务器
7. 使用 HTTP(S) 协议发送请求数据给 web 服务器
8. web 服务器收到数据请求之后,通过查询自己的服务器得到相应的结果,原路返回给客户端
9. 浏览器接收到数据之后通过浏览器自己的渲染功能来显示这个网页
10. 浏览器关闭 TCP 连接,即 4 次挥手结束,完成整个访问过程

注意:
如果本地电脑即客户端,配置了hosts,那么优先搜索hosts中配置的域名对应的ip
以Linux为例,hosts文件路径为 /etc/hosts,如果此文件中有如下的内容则上述步骤中 就不会查询DNS服务器获取www.baidu.com的ip而是
直接使用此文件中配置的ip
127.0.0.1	www.baidu.com

028.HTTP 协议状态码有什么用,列出你知道的 HTTP 协议的状态码,然后讲出他们都表示什么意思?

参考答案:
通过状态码告诉客户端服务器的执行状态,以判断下一步该执行什么操作。

常见的状态机器码有:
100-199:表示服务器成功接收部分请求,要求客户端继续提交其余请求才能完成整个处理过程
200-299:表示服务器成功接收请求并已完成处理过程
300-399:为完成请求,客户需要进一步细化请求
400-499:客户端请求有错误
500-599:服务器端出现错误

举例:
200 OK - [GET/PUT]:服务器成功返回用户请求的数据
201 CREATED - [POST]:用户新建数据成功。
204 NO CONTENT - [DELETE]:用户删除数据成功。
400 INVALID REQUEST - [POST/PUT]:用户发出的请求有错误,服务器没有进行新建或修改数据的操作
403 :拒绝访问,例如使用GET向只允许POST请求的URL发送数据
404 NOT FOUND - [*]:用户发出的请求针对的是不存在的记录,服务器没有进行操作,该操作是幂等的。。
500 INTERNAL SERVER ERROR - [*]:服务器发生错误,用户将无法判断发出的请求是否成功。

029.说说 HTTP 和 HTTPS 区别?

"""
参考答案:
HTTP 协议传输的数据都是未加密的,也就是明文的,因此使用 HTTP 协议传输隐私信息非常不安全,为了保证这些隐私数据能加密传输,
于是网景公司设计了 SSL(Secure Sockets Layer)协议用于对 HTTP 协议传输的数据进行密,从而就诞生了 HTTPS。

简单来说,HTTPS 协议是由 SSL+HTTP 协议构建的可进行加密传输、身份认证的网络协议,要比 HTTP 协议安全。

HTTPS 和 HTTP 的区别主要如下:
1、HTTPS 协议需要到 ca 申请证书,一般免费证书较少,因而需要一定费用。
2、HTTP 是超文本传输协议,信息是明文传输,HTTPS 则是具有安全性的 ssl 加密传输协议。
3、HTTP 和 HTTPS 使用的是完全不同的连接方式,用的端口也不一样,前者是 80,后者是 443。
4、HTTP 的连接很简单,是无状态的;HTTPS 协议是由 SSL+HTTP 协议构建的可进行加密传输、身份认证的网络协议,比 HTTP 协议安全。
"""

030.HTTP 请求方法都有什么?

"""
参考答案:
根据 HTTP 标准,HTTP 请求可以使用多种请求方法。
HTTP1.0 定义了三种请求方法: GET, POST 和 HEAD 方法。
HTTP1.1 新增了五种请求方法:OPTIONS, PUT, DELETE, TRACE 和 CONNECT 方法。
1. GET 请求指定的页面信息,并返回实体主体。
2. HEAD 类似于 GET 请求,只不过返回的响应中没有具体的内容,用于获取报头
3. POST 向指定资源提交数据进行处理请求(例如提交表单或者上传文件)。数据被包含在请求体中。
   POST 请求可能会导致新的资源的建立和/或已有资源的修改。
4. PUT 从客户端向服务器传送的数据取代指定的文档的内容。
5. DELETE 请求服务器删除指定的页面。
6. CONNECT HTTP/1.1 协议中预留给能够将连接改为管道方式的代理服务器。
7. OPTIONS 允许客户端查看服务器的性能。
8. TRACE 回显服务器收到的请求,主要用于测试或诊断。

经常使用的如下:
GET:获取数据
POST:提交新数据
PUT:修改数据
DELETE:删除数据
OPTIONS:其他的功能选项,例如跨域请求时,对方服务器是否允许等

"""

031.问题:cookie 和 session 的区别?

"""
参考答案:
1. cookie 数据存放在客户的浏览器上,session 数据放在服务器上。
2. cookie 不是很安全,别人可以分析存放在本地的 cookie 并进行 cookie 欺骗考虑到安全应当使用 session。
3. session 会在一定时间内保存在服务器上。当访问增多,会比较占用服务器的性能考虑到减轻服务器性能方面
4. 单个 cookie 保存的数据不能超过 4K,很多浏览器都限制一个站点最多保存 20 个 cookie(不同的浏览器可能又有自己的实现)
5. 建议: 将登陆信息等重要信息存放为 SESSION,其他信息如果需要保留,可以放在 cookie 中

Cookie 和 Session 的区别
安全性: Session 比 Cookie 安全,Session 是存储在服务器端的,Cookie 是存储在客户端的。
存取值的类型不同:Cookie 只支持存字符串数据,Session 可以存任意数据类型。
有效期不同: Cookie 可设置为长时间保持,比如我们经常使用的默认登录功能,Session 一般失效时间较短,客户端关闭(默认情况下)或者 Session 超时都会失效。
存储大小不同: 单个 Cookie 保存的数据不能超过 4K,Session 可存储数据远高于 Cookie,但是当访问量过多,会占用过多的服务器资源。

"""

在这里插入图片描述

032.HTTP常见请求头?

"""
参考答案:
1. Host (主机和端口号)
2. Connection (链接类型)
3. Upgrade-Insecure-Requests (升级为 HTTPS 请求)
4. User-Agent (浏览器名称版本等)
5. Accept (传输文件类型)
6. Referer (页面跳转处)
7. Accept-Encoding(文件编解码格式)
8. Cookie (Cookie)
9. x-requested-with :XMLHttpRequest (是 Ajax 异步请求)
"""

033.正则表达式常用规则有哪些?

"""
参考答案:

1. 括号
[] 方括号:内包括需要匹配的字符
{} 花括号:指定匹配字符的数量
() 圆括号:用来分组

2. 开始结束符号
^:表示开始
$:表示结束

3. 一些快捷命令
.:代替任意1个字符(除了\n)
\d:代替[0-9],表示匹配0至9的数字
\w:代替[a-z][0-9][_]
\s:代表任意空白符
*:0次或多次发生
+:至少一次发生
?:0或1次发生

"""

034.常见的关系型数据库管理系统产品有?

"""
参考答案:
Oracle、SQL Server、MySQL、PostgreSQL等

他们4个的区别可以参考地址:
https://www.cnblogs.com/guoguochong/p/7651836.html
"""

035.什么是事务?及其特性?

"""
参考答案:

事务:是一系列的数据库操作,是数据库应用的基本逻辑单位。
简单理解:在事务里的操作,要么全部成功,要么全部失败。

特性:
(1)原子性:即不可分割性,事务要么全部被执行,要么就全部不被执行。
(2)一致性:事务的执行使得数据库从一种正确状态转换成另一种正确状态
(3)隔离性:在事务正确提交之前,不允许把该事务对数据的任何改变提供给任何其他事务,
(4)持久性:事务正确提交后,其结果将永久保存在数据库中,即使在事务提交后有了其他故障,事务的处理结果也会得到保存。

"""

036.数据库中什么是锁?

"""
数据库是一个多用户使用的共享资源。
当多个用户并发地存取数据时,在数据库中就会产生多个事务同时存取同一数据的情况。
若对并发操作不加控制就可能会读取和存储不正确的数据,破坏数据库的一致性。

加锁是实现数据库并发控制的一个非常重要的技术。
当事务在对某个数据对象进行操作前,先向系统发出请求,对其加锁。
加锁后事务就对该数据对象有了一定的控制,在该事务释放锁之前,其他的事务不能对此数据对象进行更新操作。

基本锁类型:锁包括行级锁和表级锁

MySQL不同的存储引擎支持不同的锁机制,所有的存储引擎都以自己的方式显现了锁机制,服务器层完全不了解存储引擎中的锁实现:
MyISAM 和 MEMORY 存储引擎采用的是表级锁(table-level locking)
InnoDB 存储引擎既支持行级锁(row-level locking),也支持表级锁,但默认情况下是采用行级锁。
"""

037.MySQL中MyISAM与InnoDB 的区别,至少5点

"""
参考答案:
1. InnoDB支持事务,MyISAM不支持,对于InnoDB每一条SQL语言都默认封装成事务,自动提交,这样会影响速度,所以最好把多条SQL语言放在begin和commit之间,组成一个事务;
2. InnoDB支持行级锁,而MyISAM支持表级锁
3. InnoDB不保存表的具体行数,执行select count(*) from table时需要全表扫描。而MyISAM用一个变量保存了整个表的行数,执行上述语句时只需要读出该变量即可,速度很快(注意不能加有任何WHERE条件);
4. InnoDB支持外键,而MyISAM不支持。对一个包含外键的InnoDB表转为MYISAM会失败;
5. InnoDB表必须有主键(用户没有指定的话会自己找或生产一个主键),而MyISAM可以没有

查看MySQL的引擎:
show engines;
"""

038.MySQL中char和varchar的区别?

"""
参考答案:
char是一种固定长度的类型,varchar则是一种可变长度的类型。
char(100)类型的数据列里,每个值都占用100个字节,如果某个长度小于M,MySQL就会在它的右边用空格字符补足。(在检索操作中那些填补出来的空格字符将被去掉)。
varchar(100)类型的数据列里,每个值只占用刚好够用的字节再加上一个用来记录其长度的字节(即总长度为L+1字节)。
"""

039.主键、外键和索引的区别?

"""
参考答案:
主键、外键和索引的区别

定义的不同:
主键:唯一标识一条记录,不能有重复的,不允许为空
外键:表的外键是另一表的主键, 外键可以有重复的, 可以是空值
索引:该字段没有重复值,但可以有一个空值

作用的不同:
主键:用来保证数据完整性
外键:用来和其他表建立联系用的
索引:是提高查询排序的速度

个数的不同:
主键:一个表主键只能有一个
外键:一个表可以有多个外键
索引:一个表可以有多个唯一索引
"""

040.索引的作用?和它的优点缺点是什么?

"""
作用:
索引就一种特殊的查询表,数据库的搜索引擎可以利用它加速对数据的检索。
它很类似与现实生活中书的目录,不需要查询整本书内容就可以找到想要的数据。
索引是唯一的,创建索引允许指定单个列或者是多个列。
缺点:
减慢了数据录入的速度,同时也增加了数据库的存储空间
"""

041.如何通俗地理解三个范式?

"""
参考答案:
第一范式: 确保每列的原子性(强调的是列的原子性,即列不能够再分成其他几列).
第二范式: 在第一范式的基础上更进一层,目标是确保表中的每列都和主键相关(一是表必须有一个主键;二是没有包含在主键中的列必须完全依赖于主键,而不能只依赖于主键的部分)
第三范式: 在第二范式的基础上更进一层,目标是确保每列都和主键列直接相关,而不是间接相关(另外非主键列必须直接依赖于主键,不能存在传递依赖).
"""

042.游标是什么?

"""
参考答案:
1. 游标实际上是一种能从包括多条数据记录的结果集中每次提取一条记录的机制
2. 尽管游标能遍历结果中的所有行,但他一次只指向一行
3. 游标只能向一方向前进,并且不可以跳过任何一行数据
4. 可以对结果集当前行做修改
6. 一般不使用游标,但是需要逐条处理数据的时候,游标显得十分重要

游标的优点:
游标是针对行操作的,对从数据库中 select 查询得到的结果集的 每一行可以进行分开的独立的相同或者不相同的操作,是一种分离的思想。

游标的缺点:
性能不高,只能一行一行操作,使用游标会产生死锁,造成内存开销大
"""

043.数据库的乐观锁和悲观锁是什么?

"""
问题:数据库的乐观锁和悲观锁是什么?

参考答案:
数据库管理系统(DBMS)中的并发控制的任务是确保在多个事务同时存取数据库中同一数据时不破坏事务的隔离性和统一性以及数据库的统一性。
乐观并发控制(乐观锁)和悲观并发控制(悲观锁)是并发控制主要采用的技术手段。

悲观锁(Pessimistic Lock):
顾名思义,就是很悲观,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,
这样别人想拿这个数据就会block直到它拿到锁。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,
都是在做操作之前先上锁

乐观锁(Optimistic Lock):
顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,
可以使用版本号等机制。乐观锁适用于多读的应用类型,这样可以提高吞吐量
"""

044.WSGI是什么?有什么用?

"""
参考答案:
WSGI是:Web Server Gateway Interface (或简称 WSGI,读作“wizgy”)
作用:用来规范web服务器与web框架之间调用方式

参考网址:http://www.codetutor.top/docs/01-learn/01-python/08-mini-web/01/01-WSGI.html#_2-em-color-2ff700-wsgi-endem
"""

045.谈一谈uWSGI、WSGI、uwsgi的区别

"""
参考答案:
WSGI: 是一种Web服务器网关接口。它是一个Web服务器(如nginx,uWSGI等服务器)与web应用(如用Flask框架写的程序)通信的一种规范
uwsgi:是一种线路协议而不是通信协议,常用于在uWSGI服务器与其他网络服务器的数据通信。uwsgi协议是一个uWSGI服务器自有的协议,它与WSGI相比是两样东西
uWSGI:是实现了uwsgi和WSGI两种协议的Web服务器
"""

046.部署web项目时,为什么要用Nginx

"""
参考答案:
一个普通的个人网站,访问量不大的话,当然可以由uWSGI和Django构成。
但是一旦访问量过大,客户端请求连接就要进行长时间的等待。这个时候就出来了分布式服务器,我们可以多来几台web服务器,都能处理请求。
但是谁来分配客户端的请求连接和web服务器呢?
Nginx就是这样一个管家的存在,由它来分配。这也就是由Nginx实现反向代理,即代理服务器。
"""

047.说一说图片验证码的作用,以及整个流程

"""
问题:说一说图片验证码的作用,以及整个流程

参考答案:
http://www.codetutor.top/docs/01-learn/01-python/11-flask%E6%A1%86%E6%9E%B6/06-%E5%9B%BE%E7%89%87%E9%AA%8C%E8%AF%81%E7%A0%81/04-%E4%B8%AA%E4%BA%BA%E7%AE%80%E5%8E%86%E7%99%BB%E5%BD%95%E6%97%B6%E6%B7%BB%E5%8A%A0%E5%9B%BE%E7%89%87%E9%AA%8C%E8%AF%81%E7%A0%81.html

一个网站在登录或者关键的位置,需要验证此时的操作是否是用户真正的操作,而不是机器人,所以需要用一个手段来验证区分真正的用户
此时,就发明了 验证码

当今较为流程的验证码有3种
1. 图片验证码(字母、字符、数字的那种,还有1种是需要用户点击 或者 选择等需要操作的 有很多人员将其称为 极验验证码)
2. 短信验证码
3. 二次验证,例如在GitHub网站有一个二次验证,这个二次验证是采用的 app加密令牌的方式
"""

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GfvtUtxi-1591786857139)(C:\Users\86176\Desktop\应用\文件\python_interview_questions\065\Xnip2020-05-05_17-18-05.png)]

048.CSRF、XSS是什么?

"""
参考答案:
CSRF:(Cross-site request forgery)
1. 中文名:跨站请求伪造
2. 参考网址:http://www.codetutor.top/docs/01-learn/01-python/11-flask框架/10-CSRF、数据库迁移/01-CSRF.html
3. 要完成一次CSRF攻击,受害者必须满足两个必要的条件:
(1)登录受信任网站A,并在本地生成Cookie。(如果用户没有登录网站A,那么网站B在诱导的时候,请求网站A的api接口时,会提示你登录)
(2)在不登出A的情况下,访问危险网站B(其实是利用了网站A的漏洞)
4. 如何防御
方法一(用的最多):Token(中文翻译为 令牌) 验证
(1)服务器发送给客户端一个token
(2)客户端提交的表单中带着这个token
(3)如果这个 token 不合法,那么服务器拒绝这个请求
方法二、Referer 验证
Referer 指的是页面请求来源。意思是,只接受本站的请求,服务器才做响应;如果不是,就拦截。

XSS:(Cross Site Scripting)
1. 中文名:跨域脚本攻击
2. 其原理是攻击者向有XSS漏洞的网站中输入恶意的 HTML 代码,当用户浏览该网站时,这段 HTML 代码会自动执行,从而达到攻击的目的
3. XSS 攻击类似于 SQL 注入攻击,SQL注入攻击中以SQL语句作为用户输入,从而达到查询/修改/删除数据的目的,
    而在xss攻击中,通过插入恶意脚本,实现对用户游览器的控制,获取用户的一些信息
    XSS是 Web 程序中常见的漏洞,XSS 属于被动式且用于客户端的攻击方式
4. 如何防御
解决方法:
(1)后端程序对用户提交的参数(非法的 HTML 代码包括单双引号等)进行转义
(2)通过正则表达式过滤传入参数的html标签来防范XSS攻击
5. 案例说明
何谓转义?就是把html语言的关键字过滤掉。例如,<div>就是html的关键字,如果要在html页面上呈现<div>,其源代码就必须是&lt;div&gt;
PS:转义其实就是把HTML代码给转换成HTML实体了!
默认情况下,Django自动为开发者提供escape(转义的意思)功能,即在html代码render之前,先进行转义,然后再呈现出来。这样的话,我们如果想输出一个链接,被转义之后,可能就无法得到我们想要的结果。
例如
def view(request):
    ...省略...
    page_html = "<a href='#'>首页</a>"
    ret = {"page_html": page_html}
    return render(request, "demo.html", ret)

在demo.html文件中,如果这样使用上面的方法:
{{page_html}}

这样,输出的结果不是一个链接,而是上面链接转义后的原文。我们无法得到我们想要的结果。

有以下几种方法解决自动转义的问题:
(1)在demo.html中,使用safe过滤器
    {{ page_html|safe }}
(2)使用方法函数mark_safe
使用mark_safe函数标记后,Django将不再对该函数的内容进行转义,上面的get_username可以修改为:

from django.utils.safestring import mark_safe

def view(request):
    ...省略...
    # page_html = "<a href='#'>首页</a>"
    page_html = mark_safe("<a href='#'>首页</a>")
    ret = {"page_html": page_html}
    return render(request, "demo.html", ret)

"""

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LtLAmxtM-1591786857140)(C:\Users\86176\Desktop\应用\文件\python_interview_questions\067\CSRF跨站伪造请求.png)]

049.谈谈Cookie、Session、Token、JWT、CORS

"""
参考答案:
Cookie、Session:
已经在031题目讲解过,请移步浏览
Token:
1. 中文翻译为"令牌"
2. Acesss Token:
访问资源接口(API)时所需要的资源凭证
简单 token 的组成: uid(用户唯一的身份标识)、time(当前时间的时间戳)、sign(签名,token 的前几位以哈希算法压缩成的一定长度的十六进制字符串)
3. 服务器对 Token 的存储方式:
存到MySQL中,每次客户端请求的时候取出来验证(服务端有状态)
存到Redis中,设置过期时间,每次客户端请求的时候取出来验证(服务端有状态)
不存,每次客户端请求的时候根据之前的生成方法再生成一次来验证(JWT,服务端无状态)
4. 特点:
可扩展性好
支持移动端设备
安全
支持跨程序调用
5. token 的身份验证流程:参考当前路径下的图片"验证token.jpg"
(1)客户端使用用户名跟密码请求登录
(2)服务端收到请求,去验证用户名与密码
(3)验证成功后,服务端会签发一个 token 并把这个 token 发送给客户端
(4)客户端收到 token 以后,会把它存储起来,比如放在 cookie 里或者 localStorage 里
(5)客户端每次向服务端请求资源的时候需要带着服务端签发的 token
(6)服务端收到请求,然后去验证客户端请求里面带着的 token ,如果验证成功,就向客户端返回请求的数据
注意:
每一次请求都需要携带 token,需要把 token 放到 HTTP 的 Header 里
token 完全由应用管理,所以它可以避开同源策略
6. Token 和 Session 的区别
Session 是一种记录服务器和客户端会话状态的机制,使服务端有状态化,可以记录会话信息。
而 Token 是令牌,访问资源接口(API)时所需要的资源凭证。Token 使服务端无状态化,不会存储会话信息。
Session 和 Token 并不矛盾,作为身份认证 Token 安全性比 Session 好,因为每一个请求都有签名还能防止监听以及重复攻击。
如果你需要实现有状态的会话,仍然可以增加 Session 来在服务器端保存一些状态。
如果你的用户数据需要和第三方共享,或者允许第三方调用 API 接口,用 Token;如果永远只是自己的网站,自己的 App,用什么就无所谓了。


JWT:
1. JSON Web Token(简称 JWT)是目前最流行的跨域认证解决方案(使用JWT可以实现单点登录)
2. 是一种认证授权机制
3. JWT 认证流程:
(1)用户输入用户名/密码登录,服务端认证成功后,会返回给客户端一个 JWT
(2)客户端将 token 保存到本地(通常使用 localstorage,也可以使用 cookie)
(3)当用户希望访问一个受保护的路由或者资源的时候,需要请求头的 Authorization 字段中使用Bearer 模式添加 JWT,
    其内容看起来是下面这样
    Authorization: Bearer <token>
(4)服务端的保护路由将会检查请求头 Authorization 中的 JWT 信息,如果合法,则允许用户的行为
(5)因为 JWT 是自包含的(内部包含了一些会话信息),因此减少了需要查询数据库的需要
(6)因为 JWT 并不使用 Cookie 的,所以你可以使用任何域名提供你的 API 服务而不需要担心跨域问题
(7)因为用户的状态不再存储在服务端的内存中,所以这是一种无状态的认证机制
4. JWT的组成
参考:http://www.codetutor.top/docs/01-learn/01-python/04-drf-meiduo/02-%E7%AE%A1%E7%90%86%E5%91%98%E7%99%BB%E5%BD%95/01-JWT%E8%AE%A4%E8%AF%81%E6%9C%BA%E5%88%B6.html#_4-jwt-token%E6%95%B0%E6%8D%AE%E6%A0%BC%E5%BC%8F

Token 和 JWT 的区别
相同:
(1)都是访问资源的令牌
(2)都可以记录用户的信息
(3)都是使服务端无状态化
(4)都是只有验证成功后,客户端才能访问服务端上受保护的资源
区别:
(1)Token:服务端验证客户端发送过来的 Token 时,还需要查询数据库获取用户信息,然后验证 Token 是否有效。
(2)JWT: 服务端只需要使用密钥解密进行校验(校验也是 JWT 自己实现的)即可,不需要查询或者减少查询数据库,因为 JWT 自包含了用户信息和加密的数据。


要注意的问题
1.使用 session 时需要考虑的问题
(1)将 session 存储在服务器里面,当用户同时在线量比较多时,这些 session 会占据较多的内存,需要在服务端定期的去清理过期的 session
(2)当网站采用集群部署的时候,会遇到多台 web 服务器之间如何做 session 共享的问题。因为 session 是由单个服务器创建的,但是处理用户请求的服务器不一定是那个创建 session 的服务器,那么该服务器就无法拿到之前已经放入到 session 中的登录凭证之类的信息了。
(3)当多个应用要共享 session 时,除了以上问题,还会遇到跨域问题,因为不同的应用可能部署的主机不一样,需要在各个应用做好 cookie 跨域的处理。
(4)sessionId 是存储在 cookie 中的,假如浏览器禁止 cookie 或不支持 cookie 怎么办? 一般会把 sessionId 跟在 url 参数后面即重写 url,所以 session 不一定非得需要靠 cookie 实现
(5)移动端对 cookie 的支持不是很好,而 session 需要基于 cookie 实现,所以移动端常用的是 token
2. 使用 JWT 时需要考虑的问题
(1)因为 JWT 并不依赖 Cookie 的,所以你可以使用任何域名提供你的 API 服务而不需要担心跨域问题
(2)JWT 默认是不加密,但也是可以加密的。生成原始 Token 以后,可以用密钥再加密一次。
(3)JWT 不加密的情况下,不能将秘密数据写入 JWT。
(4)JWT 不仅可以用于认证,也可以用于交换信息。有效使用 JWT,可以降低服务器查询数据库的次数。
(5)JWT 最大的优势是服务器不再需要存储 Session,使得服务器认证鉴权业务可以方便扩展。但这也是 JWT 最大的缺点:由于服务器不需要存储 Session 状态,因此使用过程中无法废弃某个 Token 或者更改 Token 的权限。也就是说一旦 JWT 签发了,到期之前就会始终有效,除非服务器部署额外的逻辑。
(6)JWT 本身包含了认证信息,一旦泄露,任何人都可以获得该令牌的所有权限。为了减少盗用,JWT的有效期应该设置得比较短。对于一些比较重要的权限,使用时应该再次对用户进行认证。
(7)JWT 适合一次性的命令认证,颁发一个有效期极短的 JWT,即使暴露了危险也很小,由于每次操作都会生成新的 JWT,因此也没必要保存 JWT,真正实现无状态。
(8)为了减少盗用,JWT 不应该使用 HTTP 协议明码传输,要使用 HTTPS 协议传输。


CORS:
1. 中文名:跨域资源共享
2. 参考:http://www.codetutor.top/docs/01-learn/01-python/04-drf-meiduo/02-%E7%AE%A1%E7%90%86%E5%91%98%E7%99%BB%E5%BD%95/04-CORS%E8%B7%A8%E5%9F%9F.html#_2-%E8%B7%A8%E5%9F%9F%E8%AF%B7%E6%B1%82


"""

050.怎样绑定邮箱地址

"""
大体的逻辑过程:
1. 向用户的邮箱地址 发送一封带有特殊URL的 邮件
2. 如果这个邮箱地址是当前用户的,那么他就应该能够登陆这个邮箱,且收到刚刚发送的邮件
3. 用户点击邮件中的URL,会自动调用浏览器向我们的服务器发起请求
4. 服务器接收到请求之后,验证这个URL 是否是正确的,如果是 则认为 这个邮箱地址 是当前用户的,否则 让用户重新填写

参考:Django项目发送邮件的课件
http://www.codetutor.top/docs/01-learn/01-python/13-Django%E9%A1%B9%E7%9B%AE-%E7%BE%8E%E5%A4%9A%E5%95%86%E5%9F%8E%E5%89%8D%E5%8F%B0/06-%E4%B8%AA%E4%BA%BA%E4%B8%AD%E5%BF%83/02-%E4%B8%AA%E4%BA%BA%E4%BF%A1%E6%81%AF/05-%E5%8F%91%E9%80%81%E9%82%AE%E4%BB%B6.html#_1-%E6%B7%BB%E5%8A%A0%E5%8F%91%E9%80%81%E9%82%AE%E4%BB%B6%E7%9A%84%E7%B1%BB

参考答案1:
1. 申请邮件发送API(个人可以直接使用163,企业可以采用阿里云)
2. 配置web(无论django、flask 都需要配置,只是配置方式不一样)
3. 用户登录网站后,在绑定邮箱页面中输入自己的邮箱地址,点击提交
4. 服务器接收到请求后
(1)将当前用户的id与邮箱地址 进行加密(使用TimedJSONWebSignatureSerializer模块)
(2)将上述生成的秘钥+邮箱验证的路由,组成一个URL,例如 http://xxxxx.com/verifyemail?token=秘钥
(3)调用邮箱发送API,发送包含有上述URL的邮件 给用户
5. 用户邮箱收到邮件,点击URL
6. 浏览器会访问上述的URL,服务器此时接收到请求
(1)提取出token
(2)验证token
(3)返回信息给浏览器:绑定成功

参考答案2:(推荐)
1. 申请邮件发送API(个人可以直接使用163,企业可以采用阿里云)
2. 配置web(无论django、flask 都需要配置,只是配置方式不一样)
3. 用户登录网站后,在绑定邮箱页面中输入自己的邮箱地址,点击提交
4. 服务器接收到请求后
(1)将用户的邮箱+用户名+手机号(当然可以再加入时间戳等其他信息都可以)进行 哈希,得到一个加密的唯一的字符串
(2)将上述哈希字符串,存入到Redis数据库(哈希值当做key,email当做value)
(3)将上述哈希字符串+邮箱验证的路由,组成一个URL,例如 http://xxxxx.com/verifyemail?token=秘钥
(3)调用邮箱发送API,发送包含有上述URL的邮件 给用户
5. 用户邮箱收到邮件,点击URL
6. 浏览器会访问上述的URL,服务器此时接收到请求
(1)提取出token
(2)到Redis服务器中,查询验证是否有这个token
(3)返回信息给浏览器:绑定成功


"""

051.怎样通过邮件实现密码重设功能

"""
参考答案:
1. 申请发送邮件服务的API
2. 配置web服务
3. 用户点击"忘记密码"
(1)浏览器展示一个页面
(2)引导用户,输入邮箱地址,以及验证码
(3)用户点击提交
4. 服务器收到请求
(1)验证 验证码是否有效
(2)验证 邮箱是否存在
(3)生成一个唯一不相同的哈希的字符串(可以将邮箱地址+时间戳+随机值等组成)
(4)将上述哈希字符串为key,email地址为value 存入到Redis
(5)通过API 发送带有 URL链接的邮件给 用户填写的Email,URL例如 http://xxxxx.com/verifyemail?token=秘钥
5. 用户打开邮箱,点击邮件中的URL
6. 服务器端接收到请求
(1)提取token
(2)到Redis中,判断是否有此key
(3)如果有,则:展示页面,让用户输入信息密码,提交后存储到MySQL,最后重定向到登录页面 让用户用新密码登录
(4)如果没有,则:重定向到一个页面,提醒用户链接无效
"""

052.web开发中,短信验证码是怎样实现的?怎样避免频繁发送短信验证码?

"""
参考答案:
短信验证码的流程,与图片验证的流程很相似
(1)用户点击发送验证码
(2)服务器收到请求后,生成一个随机数(例如6位数密码872412)
(3)服务器调用短信发送平台的API(例如 阿里云的短信API)
(4)将随机数存储到Redis中(可以用手机当做key,872412当做value)
(5)用户输入872412,提交
(6)服务器提取用户输入的验证码,与用此用户的手机号当做key到Redis中提取的value 进行对比;如果相同 验证码正正确,否则错误

怎样避免频繁发送短信验证码
1. 为什么要避免?
如果不加密限制发送短信的频率以及次数,会导致一些不怀好意的用户,大量恶意调用发送短信验证码的API
造成企业不必要的资金浪费,还会导致Redis缓存大量的数据,影响服务器响应速度
2. 一般在发送短信验证码之前,要先通过图片验证码
现实生活中 如果在一部新手机的登录微信,其流程也是先通过图片验证码,然后再发送短信验证码
"""

053.在使用支付宝支付的时候,当用户支付成功后,浏览器会根据支付宝页面中URL的redirect地址 跳转到 自己的服务器,从而完成服务器知道用户支付成功这种方式是否有问题?怎样解决?

在这里插入图片描述
在这里插入图片描述

054.谈一谈Redis的使用场景?

"""
参考答案:

Redis都可以干什么事儿
(1)缓存,毫无疑问这是Redis当今最为人熟知的使用场景。再提升服务器性能方面非常有效;
(2)排行榜,如果使用传统的关系型数据库来做这个事儿,非常的麻烦,而利用Redis的SortSet数据结构能够非常方便搞定;
(3)计算器/限速器,利用Redis中原子性的自增操作,我们可以统计类似用户点赞数、用户访问数等,
    这类操作如果用MySQL,频繁的读写会带来相当大的压力;
    限速器比较典型的使用场景是限制某个用户访问某个API的频率,常用的有抢购时,防止用户疯狂点击带来不必要的压力;
(4)好友关系,利用集合的一些命令,比如求交集、并集、差集等。可以方便搞定一些共同好友、共同爱好之类的功能;
(5)简单消息队列,除了Redis自身的发布/订阅模式,我们也可以利用List来实现一个队列机制,比如:到货通知、邮件发送之类的需求,不需要高可靠,但是会带来非常大的DB压力,完全可以用List来完成异步解耦;
(6)Session共享,默认Session是保存在服务器的文件中,如果是集群服务,同一个用户过来可能落在不同机器上,这就会导致用户频繁登陆;采用Redis保存Session后,无论用户落在那台机器上都能够获取到对应的Session信息。

Redis不能干什么事儿
Redis感觉能干的事情特别多,但它不是万能的,合适的地方用它事半功倍。如果滥用可能导致系统的不稳定、成本增高等问题。比如
(1)用Redis去保存用户的基本信息,虽然它能够支持持久化,但是它的持久化方案并不能保证数据绝对的落地,并且还可能带来Redis性能下降,因为持久化太过频繁会增大Redis服务的压力。
(2)简单总结就是数据量太大、数据访问频率非常低的业务都不适合使用Redis,数据太大会增加成本,访问频率太低,保存在内存中纯属浪费资源。
"""

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-33OEQocT-1591786857145)(C:\Users\86176\Desktop\应用\文件\python_interview_questions\074\Redis能做什么、不能做什么.png)]

055.什么是Redis?有什么特点?

"""
参考答案:
什么是Redis
(1)Redis 是一个使用 C 语言写成的,开源的 key-value 数据库。
(2)和Memcached类似,它支持存储的value类型相对更多
    包括string(字符串)、list(链表)、set(集合)、zset(sorted set --有序集合)和hash(哈希类型)。
    这些数据类型都支持push/pop、add/remove及取交集并集和差集及更丰富的操作,而且这些操作都是原子性的。
(3)redis支持各种不同方式的排序。
(4)与memcached一样,为了保证效率,数据都是缓存在内存中。区别的是redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,
    并且在此基础上实现了master-slave(主从)同步。
(5)目前,Vmware在资助着redis项目的开发和维护。

特点:
(1)速度快,因为数据存在内存中
(2)支持丰富数据类型,支持string,list,set,sorted set,hash
(3)支持事务,操作都是原子性,所谓的原子性就是对数据的更改要么全部执行,要么全部不执行
(4)丰富的特性:可用于缓存,消息,按key设置过期时间,过期后将会自动删除
"""

在这里插入图片描述

055.token和jwt存在什么区别

"""
参考答案:
最直观的:
    token需要查库验证token 是否有效,而JWT不用查库或者少查库,直接在服务端进行校验,并且不用查库。
    因为用户的信息及加密信息在第二部分payload和第三部分签证中已经生成,只要在服务端进行校验就行,并且校验也是JWT自己实现的

例如下面这个例子:
    现在有一个接口/viptest只能是vip用户访问,我们看看服务端如何根据Token判断用户是否有效。

    普通token版:
    1. 查库判断是否过期
    2. 查库判断时候是VIP

    JWT 版本:
    假如payload部分如下:
     {
      "exp": 1518624000,
      "isVip": true,
      "uid": 1
     }
    1. 解析JWT
    2. 判断签名是否正确,根据生成签名时使用的密钥和加密算法,只要这一步过了就说明是payload是可信的
    3. 判断JWT token是否过期,根据exp,判断是否是VIP,根据isVip
    JWT版是没有查库的,它所需要的基础信息可以直接放到JWT里,服务端只要判断签名是否正确就可以判断出该用户是否可以访问该接口,当然JWT里的内容也不是无限多的,其他更多的信息我们就可以通过id去查数据库
"""

056.QQ或者微信等第三方,是怎样实现登录网站的?

"""
参考答案:
【流程参考图片】

【文字描述如下】
用户点击第三方登录,请求获取QQ登录网址,返回QQ登录的网址及参数
请求QQ登录网址,返回登录授权页面;
授权成功后,让用户携带code和state参数,访问回调网址
回调网址页面加载时候请求获取用的openid并处理,传递code参数,根据code获取access_token,根据access_token获取openid,返回openid;
根据openid处理,如果用户绑定过QQ,直接签发jwt_token数据并返回,
如果是首次登录,则对openid进行加密并返回,请求绑定QQ的登录用户,如果手机号存在直接绑定,否则创建新用户绑定,并签发jwt_token返回,绑定成功;
"""

在这里插入图片描述

057.谈谈对Docker的理解

"""
参考答案:
在项目开发或者部署时,由于不同的技术之间的版本冲突和依赖。
例如某公司使用的是Python。用Python之后就会选择,是Python2还是Python3。开始发现有一些比较新的技术库会支持Python3,
但还有一些比较传统的代码集,它们就不能支持Python3,只支持Python2。
这就使我们开发不同的程序,或者是相同的程序用不同的库,会产生不同的问题。
导致了,我们向服务器部署代码非常困难。

解决方法就是Docker。

相对于传统方式,它能够同时交付并且部署程序所运行的环境。
相对于虚拟机,它给出了基本的程序代码去交付,也仅仅只打包必需的内容,从而减轻了负担。

Docker指令:
# 启动docker
sudo service docker start
# 停止docker
sudo service docker stop
# 重启docker
sudo service docker restart
删除
docker image rm 镜像名或镜像id
"""

058.如何设计符合 RESTful 风格的 API

"""
参考答案:
一、域名:
    将 api 部署在专用域名下:
    http://api.example.com
    或者将 api 放在主域名下:
    http://www.example.com/api/
二、版本:
    将 API 的版本号放在 url 中。
    http://www.example.com/app/1.0/info
    http://www.example.com/app/1.2/info
三、路径:
    路径表示 API 的具体网址。每个网址代表一种资源。
    资源作为网址,网址中不能有动词只能有名词,一般名词要与数据库的表名对应。而且名词要使用复数。
    # 错误示例:
    http://www.example.com/getGoods
    http://www.example.com/listOrders
    # 正确示例:
    # 获取单个商品
    http://www.example.com/app/goods/1
    # 获取所有商品
    http://www.example.com/app/goods
四、使用标准的 HTTP 方法:
    对于资源的具体操作类型,由 HTTP 动词表示。
    常用的 HTTP 动词有四个:
        GET SELECT :从服务器获取资源。
        POST CREATE :在服务器新建资源。
        PUT UPDATE :在服务器更新资源。
        DELETE DELETE :从服务器删除资源。
    示例:
    # 获取指定商品的信息
    GET http://www.example.com/goods/ID
    # 新建商品的信息
    POST http://www.example.com/goods
    # 更新指定商品的信息
    PUT http://www.example.com/goods/ID
    # 删除指定商品的信息
    DELETE http://www.example.com/goods/ID
五、过滤信息:
    如果资源数据较多,服务器不能将所有数据一次全部返回给客户端。API 应该提供参数,过滤返回结果。
    # 实例:
    # 指定返回数据的数量
    http://www.example.com/goods?limit=10
    # 指定返回数据的开始位置
    http://www.example.com/goods?offset=10
    # 指定第几页,以及每页数据的数量
    http://www.example.com/goods?page=2&per_page=20
六、状态码:
    # 服务器向用户返回的状态码和提示信息,常用的有:
    200 OK :服务器成功返回用户请求的数据或修改数据
    201 CREATED :用户新建成功。
    202 Accepted:表示请求已进入后台排队。
    400 INVALID REQUEST :用户发出的请求有错误。
    401 Unauthorized :用户没有权限。
    403 Forbidden :访问被禁止。
    404 NOT FOUND :请求针对的是不存在的记录。
    406 Not Acceptable :用户请求的的格式不正确。
    500 INTERNAL SERVER ERROR :服务器发生错误。
七、错误信息:
    一般来说,服务器返回的错误信息,以键值对的形式返回。
    {
     error: 'Invalid API KEY'
    }
八、响应结果:
    针对不同结果,服务器向客户端返回的结果应符合以下规范。
    #返回商品列表
    GET http://www.example.com/goods        状态码:200
    #返回单个商品
    GET http://www.example.com/goods/1      状态码:200
    #返回新生成的商品
    POST http://www.example.com/goods       状态码:201
    #返回修改后的商品
    PUT http://www.example.com/goods/1      状态码:200
    #返回一个空文档
    DELETE http://www.example.com/goods     状态码:204
九、使用链接关联相关的资源:
    在返回响应结果时提供链接其他 API 的方法,使客户端很方便的获取相关联的信息。
十、其他:
    服务器返回的数据格式,应该尽量使用 JSON,避免使用 XML。
"""

059.什么是 restful api,谈谈你的理解?

"""
问题:什么是 restful api,谈谈你的理解?

参考答案:
REST: Representational State Transfer 的缩写,翻译:“具象状态传输”。一般解释为“表现层状态转换”。
REST 是设计风格而不是标准。是指客户端和服务器的交互形式。我们需要关注的重点是如何设计REST 风格的网络接口。

REST 的特点:
1.具象的。一般指表现层,要表现的对象就是资源。比如,客户端访问服务器,获取的数据就是资源。比如文字、图片、音视频等。
2.表现:资源的表现形式。txt 格式、html 格式、json 格式、jpg 格式等。浏览器通过 URL 确定资源的位置,
    但是需要在 HTTP 请求头中,用 Accept 和 Content-Type 字段指定,这两个字段是对资源表现的描述。
3.状态转换:客户端和服务器交互的过程。在这个过程中,一定会有数据和状态的转化,这种转化叫做状态转换。
    其中,GET 表示获取资源,POST 表示新建资源,PUT 表示更新资源,DELETE 表示删除资源。HTTP 协议中最常用的就是这四种操作方式。

RESTful 架构:
1.每个 URL 代表一种资源;
2.客户端和服务器之间,传递这种资源的某种表现层;
3.客户端通过四个 http 动词,对服务器资源进行操作,实现表现层状态转换。
"""
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

~简

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值