• python 面试题(进阶)


    106.进程总结

    进程:程序运行在操作系统上的一个实例,就称之为进程。进程需要相应的系统资源:内存、时间片、pid。 创建进程: 首先要导入multiprocessing中的Process: 创建一个Process对象; 创建Process对象时,可以传递参数;

    p = Process(target=XXX,args=(tuple,),kwargs={key:value})
    target = XXX 指定的任务函数,不用加(),
    args=(tuple,)kwargs={key:value}给任务函数传递的参数

    使用start()启动进程 结束进程 给子进程指定函数传递参数Demo

    import os
    from mulitprocessing import Process
    import time
    
    def pro_func(name,age,**kwargs):
        for i in range(5):
            print("子进程正在运行中,name=%s,age=%d,pid=%d"%(name,age,os.getpid()))
            print(kwargs)
            time.sleep(0.2)
    if __name__ =="__main__":
        #创建Process对象
        p = Process(target=pro_func,args=('小明',18),kwargs={'m':20})
        #启动进程
        p.start()
        time.sleep(1)
        #1秒钟之后,立刻结束子进程
        p.terminate()
        p.join()

    注意:进程间不共享全局变量

    进程之间的通信-Queue

    在初始化Queue()对象时(例如q=Queue(),若在括号中没有指定最大可接受的消息数量,获数量为负值时,那么就代表可接受的消息数量没有上限一直到内存尽头)

    Queue.qsize():返回当前队列包含的消息数量

    Queue.empty():如果队列为空,返回True,反之False

    Queue.full():如果队列满了,返回True,反之False

    Queue.get([block[,timeout]]):获取队列中的一条消息,然后将其从队列中移除,

    block默认值为True。

    如果block使用默认值,且没有设置timeout(单位秒),消息队列如果为空,此时程序将被阻塞(停在读中状态),直到消息队列读到消息为止,如果设置了timeout,则会等待timeout秒,若还没读取到任何消息,则抛出“Queue.Empty"异常:

    Queue.get_nowait()相当于Queue.get(False)

    Queue.put(item,[block[,timeout]]):将item消息写入队列,block默认值为True; 如果block使用默认值,且没有设置timeout(单位秒),消息队列如果已经没有空间可写入,此时程序将被阻塞(停在写入状态),直到从消息队列腾出空间为止,如果设置了timeout,则会等待timeout秒,若还没空间,则抛出”Queue.Full"异常 如果block值为False,消息队列如果没有空间可写入,则会立刻抛出"Queue.Full"异常; Queue.put_nowait(item):相当Queue.put(item,False)

    进程间通信Demo:

    from multiprocessing import Process.Queue
    import os,time,random
    #写数据进程执行的代码:
    def write(q):
        for value in ['A','B','C']:
            print("Put %s to queue...",%value)
            q.put(value)
            time.sleep(random.random())
    #读数据进程执行的代码
    def read(q):
        while True:
            if not q.empty():
                value = q.get(True)
                print("Get %s from queue.",%value)
                time.sleep(random.random())
            else:
                break
    if __name__=='__main__':
        #父进程创建Queue,并传给各个子进程
        q = Queue()
        pw = Process(target=write,args=(q,))
        pr = Process(target=read,args=(q,))
        #启动子进程pw ,写入:
        pw.start()
        #等待pw结束
        pw.join()
        #启动子进程pr,读取:
        pr.start()
        pr.join()
        #pr 进程里是死循环,无法等待其结束,只能强行终止:
        print('')
        print('所有数据都写入并且读完')
    进程池Pool
    
    #coding:utf-8
    from multiprocessing import Pool
    import os,time,random
    
    def worker(msg):
        t_start = time.time()
        print("%s 开始执行,进程号为%d"%(msg,os.getpid()))
        # random.random()随机生成0-1之间的浮点数
        time.sleep(random.random()*2)
        t_stop = time.time()
        print(msg,"执行完毕,耗时%0.2f”%(t_stop-t_start))
    
    po = Pool(3)#定义一个进程池,最大进程数3
    for i in range(0,10):
        po.apply_async(worker,(i,))
    print("---start----")
    po.close()
    po.join()
    print("----end----")

    进程池中使用Queue

    如果要使用Pool创建进程,就需要使用multiprocessing.Manager()中的Queue(),而不是multiprocessing.Queue(),否则会得到如下的错误信息:

    RuntimeError: Queue objects should only be shared between processs through inheritance

    from multiprocessing import Manager,Pool
    import os,time,random
    def reader(q):
        print("reader 启动(%s),父进程为(%s)"%(os.getpid(),os.getpid()))
        for i in range(q.qsize()):
            print("reader 从Queue获取到消息:%s"%q.get(True))
    
    def writer(q):
        print("writer 启动(%s),父进程为(%s)"%(os.getpid(),os.getpid()))
        for i ini "itcast":
            q.put(i)
    if __name__ == "__main__":
        print("(%s)start"%os.getpid())
        q = Manager().Queue()#使用Manager中的Queue
        po = Pool()
        po.apply_async(wrtier,(q,))
        time.sleep(1)
        po.apply_async(reader,(q,))
        po.close()
        po.join()
        print("(%s)End"%os.getpid())

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

    这个问题被问的概念相当之大, 进程:一个运行的程序(代码)就是一个进程,没有运行的代码叫程序,进程是系统资源分配的最小单位,进程拥有自己独立的内存空间,所有进程间数据不共享,开销大。

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

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

    108.Python异步使用场景有那些?

    异步的使用场景:

    1、 不涉及共享资源,获对共享资源只读,即非互斥操作

    2、 没有时序上的严格关系

    3、 不需要原子操作,或可以通过其他方式控制原子性

    4、 常用于IO操作等耗时操作,因为比较影响客户体验和使用性能

    5、 不影响主线程逻辑

    109.多线程共同操作同一个数据互斥锁同步?

    import threading
    import time
    class MyThread(threading.Thread):
        def run(self):
            global num
            time.sleep(1)
        
            if mutex.acquire(1):
                num +=1
                msg = self.name + 'set num to ' +str(num)
                print msg
                mutex.release()
    num = 0
    mutex = threading.Lock()
    def test():
        for i in range(5):
            t = MyThread()
            t.start()
    if __name__=="__main__":
        test()

    110.什么是多线程竞争?

    线程是非独立的,同一个进程里线程是数据共享的,当各个线程访问数据资源时会出现竞争状态即:数据几乎同步会被多个线程占用,造成数据混乱,即所谓的线程不安全

    那么怎么解决多线程竞争问题?---锁

    锁的好处: 确保了某段关键代码(共享数据资源)只能由一个线程从头到尾完整地执行能解决多线程资源竞争下的原子操作问题。

    锁的坏处: 阻止了多线程并发执行,包含锁的某段代码实际上只能以单线程模式执行,效率就大大地下降了

    锁的致命问题: 死锁

    111.请介绍一下Python的线程同步?

    一、 setDaemon(False) 当一个进程启动之后,会默认产生一个主线程,因为线程是程序执行的最小单位,当设置多线程时,主线程会创建多个子线程,在Python中,默认情况下就是setDaemon(False),主线程执行完自己的任务以后,就退出了,此时子线程会继续执行自己的任务,直到自己的任务结束。

    例子

    import threading 
    import time
    
    def thread():
        time.sleep(2)
        print('---子线程结束---')
    
    def main():
        t1 = threading.Thread(target=thread)
        t1.start()
        print('---主线程--结束')
    
    if __name__ =='__main__':
        main()
    #执行结果
    ---主线程--结束
    ---子线程结束---

    二、 setDaemon(True) 当我们使用setDaemon(True)时,这是子线程为守护线程,主线程一旦执行结束,则全部子线程被强制终止

    例子

    import threading
    import time
    def thread():
        time.sleep(2)
        print(’---子线程结束---')
    def main():
        t1 = threading.Thread(target=thread)
        t1.setDaemon(True)#设置子线程守护主线程
        t1.start()
        print('---主线程结束---')
    
    if __name__ =='__main__':
        main()
    #执行结果
    ---主线程结束--- #只有主线程结束,子线程来不及执行就被强制结束

    三、 join(线程同步) join 所完成的工作就是线程同步,即主线程任务结束以后,进入堵塞状态,一直等待所有的子线程结束以后,主线程再终止。

    当设置守护线程时,含义是主线程对于子线程等待timeout的时间将会杀死该子线程,最后退出程序,所以说,如果有10个子线程,全部的等待时间就是每个timeout的累加和,简单的来说,就是给每个子线程一个timeou的时间,让他去执行,时间一到,不管任务有没有完成,直接杀死。

    没有设置守护线程时,主线程将会等待timeout的累加和这样的一段时间,时间一到,主线程结束,但是并没有杀死子线程,子线程依然可以继续执行,直到子线程全部结束,程序退出。

    例子

    import threading
    import time
    
    def thread():
        time.sleep(2)
        print('---子线程结束---')
    
    def main():
        t1 = threading.Thread(target=thread)
        t1.setDaemon(True)
        t1.start()
        t1.join(timeout=1)#1 线程同步,主线程堵塞1s 然后主线程结束,子线程继续执行
                            #2 如果不设置timeout参数就等子线程结束主线程再结束
                            #3 如果设置了setDaemon=True和timeout=1主线程等待1s后会强制杀死子线程,然后主线程结束
        print('---主线程结束---')
    
    if __name__=='__main___':
        main()

    112.解释以下什么是锁,有哪几种锁?

    锁(Lock)是python提供的对线程控制的对象。有互斥锁,可重入锁,死锁。

    113.什么是死锁?

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

    GIL锁 全局解释器锁

    作用: 限制多线程同时执行,保证同一时间只有一个线程执行,所以cython里的多线程其实是伪多线程!

    所以python里常常使用协程技术来代替多线程,协程是一种更轻量级的线程。

    进程和线程的切换时由系统决定,而协程由我们程序员自己决定,而模块gevent下切换是遇到了耗时操作时才会切换

    三者的关系:进程里有线程,线程里有协程。

    114.多线程交互访问数据,如果访问到了就不访问了?

    怎么避免重读?

    创建一个已访问数据列表,用于存储已经访问过的数据,并加上互斥锁,在多线程访问数据的时候先查看数据是否在已访问的列表中,若已存在就直接跳过。

    115.什么是线程安全,什么是互斥锁?

    每个对象都对应于一个可称为’互斥锁‘的标记,这个标记用来保证在任一时刻,只能有一个线程访问该对象。

    同一进程中的多线程之间是共享系统资源的,多个线程同时对一个对象进行操作,一个线程操作尚未结束,另一线程已经对其进行操作,导致最终结果出现错误,此时需要对被操作对象添加互斥锁,保证每个线程对该对象的操作都得到正确的结果。

    116.说说下面几个概念:同步,异步,阻塞,非阻塞?

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

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

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

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

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

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

    孤儿进程: 父进程退出,子进程还在运行的这些子进程都是孤儿进程,孤儿进程将被init 进程(进程号为1)所收养,并由init 进程对他们完成状态收集工作。

    僵尸进程: 进程使用fork 创建子进程,如果子进程退出,而父进程并没有调用wait 获waitpid 获取子进程的状态信息,那么子进程的进程描述符仍然保存在系统中的这些进程是僵尸进程。

    避免僵尸进程的方法:

    1.fork 两次用孙子进程去完成子进程的任务

    2.用wait()函数使父进程阻塞

    3.使用信号量,在signal handler 中调用waitpid,这样父进程不用阻塞

    118.python中进程与线程的使用场景?

    多进程适合在CPU密集操作(cpu操作指令比较多,如位多的的浮点运算)。

    多线程适合在IO密性型操作(读写数据操作比多的的,比如爬虫)

    119.线程是并发还是并行,进程是并发还是并行?

    线程是并发,进程是并行;

    进程之间互相独立,是系统分配资源的最小单位,同一个线程中的所有线程共享资源。

    120.并行(parallel)和并发(concurrency)?

    并行: 同一时刻多个任务同时在运行

    不会在同一时刻同时运行,存在交替执行的情况。

    实现并行的库有: multiprocessing

    实现并发的库有: threading

    程序需要执行较多的读写、请求和回复任务的需要大量的IO操作,IO密集型操作使用并发更好。

    CPU运算量大的程序,使用并行会更好

    121.IO密集型和CPU密集型区别?

    IO密集型: 系统运行,大部分的状况是CPU在等 I/O(硬盘/内存)的读/写

    CPU密集型: 大部分时间用来做计算,逻辑判断等CPU动作的程序称之CPU密集型。

    122.python asyncio的原理?

    asyncio这个库就是使用python的yield这个可以打断保存当前函数的上下文的机制, 封装好了selector 摆脱掉了复杂的回调关系

    网络编程

    123.怎么实现强行关闭客户端和服务器之间的连接?

    124.简述TCP和UDP的区别以及优缺点?

    125.简述浏览器通过WSGI请求动态资源的过程?

    浏览器发送的请求被Nginx监听到,Nginx根据请求的URL的PATH或者后缀把请求静态资源的分发到静态资源的目录,别的请求根据配置好的转发到相应端口。 实现了WSGI的程序会监听某个端口,监听到Nginx转发过来的请求接收后(一般用socket的recv来接收HTTP的报文)以后把请求的报文封装成environ的字典对象,然后再提供一个start_response的方法。把这两个对象当成参数传入某个方法比如wsgi_app(environ, start_response)或者实现了__call__(self, environ, start_response)方法的某个实例。这个实例再调用start_response返回给实现了WSGI的中间件,再由中间件返回给Nginx。

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

    127.Post和Get请求的区别?

    128.cookie 和session 的区别?

    129.列出你知道的HTTP协议的状态码,说出表示什么意思?

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

    131.说一下什么是tcp的2MSL?

    132.为什么客户端在TIME-WAIT状态必须等待2MSL的时间?

    133.说说HTTP和HTTPS区别?

    134.谈一下HTTP协议以及协议头部中表示数据类型的字段?

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

    136.使用Socket套接字需要传入哪些参数 ?

    137.HTTP常见请求头?

    138.七层模型?

    139.url的形式?

    Web

    Flask

    140.对Flask蓝图(Blueprint)的理解?

    蓝图的定义

    蓝图 /Blueprint 是Flask应用程序组件化的方法,可以在一个应用内或跨越多个项目共用蓝图。使用蓝图可以极大简化大型应用的开发难度,也为Flask扩展提供了一种在应用中注册服务的集中式机制。

    蓝图的应用场景:

    把一个应用分解为一个蓝图的集合。这对大型应用是理想的。一个项目可以实例化一个应用对象,初始化几个扩展,并注册一集合的蓝图。

    以URL前缀和/或子域名,在应用上注册一个蓝图。URL前缀/子域名中的参数即成为这个蓝图下的所有视图函数的共同的视图参数(默认情况下) 在一个应用中用不同的URL规则多次注册一个蓝图。

    通过蓝图提供模板过滤器、静态文件、模板和其他功能。一个蓝图不一定要实现应用或视图函数。

    初始化一个Flask扩展时,在这些情况中注册一个蓝图。

    蓝图的缺点:

    不能在应用创建后撤销注册一个蓝图而不销毁整个应用对象。

    使用蓝图的三个步骤

    1.创建一个蓝图对象

    blue = Blueprint("blue",__name__)

    2.在这个蓝图对象上进行操作,例如注册路由、指定静态文件夹、注册模板过滤器...

    @blue.route('/')
    def blue_index():
        return "Welcome to my blueprint"

    3.在应用对象上注册这个蓝图对象

    app.register_blueprint(blue,url_prefix="/blue")

    141.Flask 和 Django 路由映射的区别?

    在django中,路由是浏览器访问服务器时,先访问的项目中的url,再由项目中的url找到应用中url,这些url是放在一个列表里,遵从从前往后匹配的规则。在flask中,路由是通过装饰器给每个视图函数提供的,而且根据请求方式的不同可以一个url用于不同的作用。

    Django

    142.什么是wsgi,uwsgi,uWSGI?

    WSGI:

    web服务器网关接口,是一套协议。用于接收用户请求并将请求进行初次封装,然后将请求交给web框架。

    实现wsgi协议的模块:wsgiref,本质上就是编写一socket服务端,用于接收用户请求(django)

    werkzeug,本质上就是编写一个socket服务端,用于接收用户请求(flask)

    uwsgi:

    与WSGI一样是一种通信协议,它是uWSGI服务器的独占协议,用于定义传输信息的类型。 uWSGI:

    是一个web服务器,实现了WSGI的协议,uWSGI协议,http协议

    143.Django、Flask、Tornado的对比?

    1、 Django走的大而全的方向,开发效率高。它的MTV框架,自带的ORM,admin后台管理,自带的sqlite数据库和开发测试用的服务器,给开发者提高了超高的开发效率。 重量级web框架,功能齐全,提供一站式解决的思路,能让开发者不用在选择上花费大量时间。

    自带ORM和模板引擎,支持jinja等非官方模板引擎。

    自带ORM使Django和关系型数据库耦合度高,如果要使用非关系型数据库,需要使用第三方库

    自带数据库管理app

    成熟,稳定,开发效率高,相对于Flask,Django的整体封闭性比较好,适合做企业级网站的开发。python web框架的先驱,第三方库丰富

    2、 Flask 是轻量级的框架,自由,灵活,可扩展性强,核心基于Werkzeug WSGI工具 和jinja2 模板引擎

    适用于做小网站以及web服务的API,开发大型网站无压力,但架构需要自己设计

    与关系型数据库的结合不弱于Django,而与非关系型数据库的结合远远优于Django

    3、 Tornado走的是少而精的方向,性能优越,它最出名的异步非阻塞的设计方式

    Tornado的两大核心模块:

    iostraem:对非阻塞的socket进行简单的封装

    ioloop: 对I/O 多路复用的封装,它实现一个单例

    144.CORS 和 CSRF的区别?

    什么是CORS?

    CORS是一个W3C标准,全称是“跨域资源共享"(Cross-origin resoure sharing). 它允许浏览器向跨源服务器,发出XMLHttpRequest请求,从而客服了AJAX只能同源使用的限制。

    什么是CSRF?

    CSRF主流防御方式是在后端生成表单的时候生成一串随机token,内置到表单里成为一个字段,同时,将此串token置入session中。每次表单提交到后端时都会检查这两个值是否一致,以此来判断此次表单提交是否是可信的,提交过一次之后,如果这个页面没有生成CSRF token,那么token将会被清空,如果有新的需求,那么token会被更新。 攻击者可以伪造POST表单提交,但是他没有后端生成的内置于表单的token,session中没有token都无济于事。

    145.Session,Cookie,JWT的理解

    为什么要使用会话管理

    众所周知,HTTP协议是一个无状态的协议,也就是说每个请求都是一个独立的请求,请求与请求之间并无关系。但在实际的应用场景,这种方式并不能满足我们的需求。举个大家都喜欢用的例子,把商品加入购物车,单独考虑这个请求,服务端并不知道这个商品是谁的,应该加入谁的购物车?因此这个请求的上下文环境实际上应该包含用户的相关信息,在每次用户发出请求时把这一小部分额外信息,也做为请求的一部分,这样服务端就可以根据上下文中的信息,针对具体的用户进行操作。所以这几种技术的出现都是对HTTP协议的一个补充,使得我们可以用HTTP协议+状态管理构建一个的面向用户的WEB应用。

    Session 和Cookie的区别

    这里我想先谈谈session与cookies,因为这两个技术是做为开发最为常见的。那么session与cookies的区别是什么?个人认为session与cookies最核心区别在于额外信息由谁来维护。利用cookies来实现会话管理时,用户的相关信息或者其他我们想要保持在每个请求中的信息,都是放在cookies中,而cookies是由客户端来保存,每当客户端发出新请求时,就会稍带上cookies,服务端会根据其中的信息进行操作。 当利用session来进行会话管理时,客户端实际上只存了一个由服务端发送的session_id,而由这个session_id,可以在服务端还原出所需要的所有状态信息,从这里可以看出这部分信息是由服务端来维护的。

    除此以外,session与cookies都有一些自己的缺点:

    cookies的安全性不好,攻击者可以通过获取本地cookies进行欺骗或者利用cookies进行CSRF攻击。使用cookies时,在多个域名下,会存在跨域问题。 session 在一定的时间里,需要存放在服务端,因此当拥有大量用户时,也会大幅度降低服务端的性能,当有多台机器时,如何共享session也会是一个问题.(redis集群)也就是说,用户第一个访问的时候是服务器A,而第二个请求被转发给了服务器B,那服务器B如何得知其状态。实际上,session与cookies是有联系的,比如我们可以把session_id存放在cookies中的。

    JWT是如何工作的

    首先用户发出登录请求,服务端根据用户的登录请求进行匹配,如果匹配成功,将相关的信息放入payload中,利用算法,加上服务端的密钥生成token,这里需要注意的是secret_key很重要,如果这个泄露的话,客户端就可以随机篡改发送的额外信息,它是信息完整性的保证。生成token后服务端将其返回给客户端,客户端可以在下次请求时,将token一起交给服务端,一般是说我们可以将其放在Authorization首部中,这样也就可以避免跨域问题。

    146.简述Django请求生命周期

    一般是用户通过浏览器向我们的服务器发起一个请求(request),这个请求会去访问视图函数,如果不涉及到数据调用,那么这个时候视图函数返回一个模板也就是一个网页给用户) 视图函数调用模型毛模型去数据库查找数据,然后逐级返回,视图函数把返回的数据填充到模板中空格中,最后返回网页给用户。

    1.wsgi ,请求封装后交给web框架(Flask,Django)

    2.中间件,对请求进行校验或在请求对象中添加其他相关数据,例如:csrf,request.session

    3.路由匹配 根据浏览器发送的不同url去匹配不同的视图函数

    4.视图函数,在视图函数中进行业务逻辑的处理,可能涉及到:orm,templates

    5.中间件,对响应的数据进行处理

    6.wsgi,将响应的内容发送给浏览器

    147.用的restframework完成api发送时间时区

    当前的问题是用django的rest framework模块做一个get请求的发送时间以及时区信息的api

    class getCurrenttime(APIView):
        def get(self,request):
            local_time = time.localtime()
            time_zone =settings.TIME_ZONE
            temp = {'localtime':local_time,'timezone':time_zone}
            return Response(temp)

    148.nginx,tomcat,apach到都是什么?

    Nginx(engine x)是一个高性能的HTTP和反向代理服务器,也是 一个IMAP/POP3/SMTP服务器,工作在OSI七层,负载的实现方式:轮询,IP_HASH,fair,session_sticky. Apache HTTP Server是一个模块化的服务器,源于NCSAhttpd服务器 Tomcat 服务器是一个免费的开放源代码的Web应用服务器,属于轻量级应用服务器,是开发和调试JSP程序的首选。

    149.请给出你熟悉关系数据库范式有哪些,有什么作用?

    在进行数据库的设计时,所遵循的一些规范,只要按照设计规范进行设计,就能设计出没有数据冗余和数据维护异常的数据库结构。

    数据库的设计的规范有很多,通常来说我们在设是数据库时只要达到其中一些规范就可以了,这些规范又称之为数据库的三范式,一共有三条,也存在着其他范式,我们只要做到满足前三个范式的要求,就能设陈出符合我们的数据库了,我们也不能全部来按照范式的要求来做,还要考虑实际的业务使用情况,所以有时候也需要做一些违反范式的要求。 1.数据库设计的第一范式(最基本),基本上所有数据库的范式都是符合第一范式的,符合第一范式的表具有以下几个特点:

    数据库表中的所有字段都只具有单一属性,单一属性的列是由基本的数据类型(整型,浮点型,字符型等)所构成的设计出来的表都是简单的二比表

    2.数据库设计的第二范式(是在第一范式的基础上设计的),要求一个表中只具有一个业务主键,也就是说符合第二范式的表中不能存在非主键列对只对部分主键的依赖关系

    3.数据库设计的第三范式,指每一个非主属性既不部分依赖与也不传递依赖于业务主键,也就是第二范式的基础上消除了非主属性对主键的传递依赖

    150.简述QQ登陆过程

    qq登录,在我们的项目中分为了三个接口,

    第一个接口是请求qq服务器返回一个qq登录的界面;

    第二个接口是通过扫码或账号登陆进行验证,qq服务器返回给浏览器一个code和state,利用这个code通过本地服务器去向qq服务器获取access_token覆返回给本地服务器,凭借access_token再向qq服务器获取用户的openid(openid用户的唯一标识)

    第三个接口是判断用户是否是第一次qq登录,如果不是的话直接登录返回的jwt-token给用户,对没有绑定过本网站的用户,对openid进行加密生成token进行绑定

    151.post 和 get的区别?

    1.GET是从服务器上获取数据,POST是向服务器传送数据

    2.在客户端,GET方式在通过URL提交数据,数据在URL中可以看到,POST方式,数据放置在HTML——HEADER内提交

    3.对于GET方式,服务器端用Request.QueryString获取变量的值,对于POST方式,服务器端用Request.Form获取提交的数据

    152.项目中日志的作用

    一、日志相关概念

    1.日志是一种可以追踪某些软件运行时所发生事件的方法

    2.软件开发人员可以向他们的代码中调用日志记录相关的方法来表明发生了某些事情

    3.一个事件可以用一个包含可选变量数据的消息来描述

    4.此外,事件也有重要性的概念,这个重要性也可以被成为严重性级别(level)

    二、日志的作用

    1.通过log的分析,可以方便用户了解系统或软件、应用的运行情况;

    2.如果你的应用log足够丰富,可以分析以往用户的操作行为、类型喜好,地域分布或其他更多信息;

    3.如果一个应用的log同时也分了多个级别,那么可以很轻易地分析得到该应用的健康状况,及时发现问题并快速定位、解决问题,补救损失。

    4.简单来讲就是我们通过记录和分析日志可以了解一个系统或软件程序运行情况是否正常,也可以在应用程序出现故障时快速定位问题。不仅在开发中,在运维中日志也很重要,日志的作用也可以简单。总结为以下几点:

    1.程序调试

    2.了解软件程序运行情况,是否正常

    3,软件程序运行故障分析与问题定位

    4,如果应用的日志信息足够详细和丰富,还可以用来做用户行为分析

    153.django中间件的使用?

    Django在中间件中预置了六个方法,这六个方法的区别在于不同的阶段执行,对输入或输出进行干预,方法如下:

    1.初始化:无需任何参数,服务器响应第一个请求的时候调用一次,用于确定是否启用当前中间件

    def __init__():
        pass

    2.处理请求前:在每个请求上调用,返回None或HttpResponse对象。

    def process_request(request):
        pass

    3.处理视图前:在每个请求上调用,返回None或HttpResponse对象。

    def process_view(request,view_func,view_args,view_kwargs):
        pass

    4.处理模板响应前:在每个请求上调用,返回实现了render方法的响应对象。

    def process_template_response(request,response):
        pass

    5.处理响应后:所有响应返回浏览器之前被调用,在每个请求上调用,返回HttpResponse对象。

    def process_response(request,response):
        pass

    6.异常处理:当视图抛出异常时调用,在每个请求上调用,返回一个HttpResponse对象。

    def process_exception(request,exception):
        pass

    154.谈一下你对uWSGI和nginx的理解?

    1.uWSGI是一个Web服务器,它实现了WSGI协议、uwsgi、http等协议。Nginx中HttpUwsgiModule的作用是与uWSGI服务器进行交换。WSGI是一种Web服务器网关接口。它是一个Web服务器(如nginx,uWSGI等服务器)与web应用(如用Flask框架写的程序)通信的一种规范。

    要注意WSGI/uwsgi/uWSGI这三个概念的区分。

    WSGI是一种通信协议。

    uwsgi是一种线路协议而不是通信协议,在此常用于在uWSGI服务器与其他网络服务器的数据通信。

    uWSGI是实现了uwsgi和WSGI两种协议的Web服务器。

    nginx 是一个开源的高性能的HTTP服务器和反向代理:

    1.作为web服务器,它处理静态文件和索引文件效果非常高

    2.它的设计非常注重效率,最大支持5万个并发连接,但只占用很少的内存空间

    3.稳定性高,配置简洁。

    4.强大的反向代理和负载均衡功能,平衡集群中各个服务器的负载压力应用

    155.Python中三大框架各自的应用场景?

    django:主要是用来搞快速开发的,他的亮点就是快速开发,节约成本,,如果要实现高并发的话,就要对django进行二次开发,比如把整个笨重的框架给拆掉自己写socket实现http的通信,底层用纯c,c++写提升效率,ORM框架给干掉,自己编写封装与数据库交互的框架,ORM虽然面向对象来操作数据库,但是它的效率很低,使用外键来联系表与表之间的查询; flask: 轻量级,主要是用来写接口的一个框架,实现前后端分离,提考开发效率,Flask本身相当于一个内核,其他几乎所有的功能都要用到扩展(邮件扩展Flask-Mail,用户认证Flask-Login),都需要用第三方的扩展来实现。比如可以用Flask-extension加入ORM、文件上传、身份验证等。Flask没有默认使用的数据库,你可以选择MySQL,也可以用NoSQL。

    其WSGI工具箱用Werkzeug(路由模块),模板引擎则使用Jinja2,这两个也是Flask框架的核心。

    Tornado: Tornado是一种Web服务器软件的开源版本。Tornado和现在的主流Web服务器框架(包括大多数Python的框架)有着明显的区别:它是非阻塞式服务器,而且速度相当快。得利于其非阻塞的方式和对epoll的运用,Tornado每秒可以处理数以千计的连接因此Tornado是实时Web服务的一个理想框架

    156.Django中哪里用到了线程?哪里用到了协程?哪里用到了进程?

    1.Django中耗时的任务用一个进程或者线程来执行,比如发邮件,使用celery.

    2.部署django项目是时候,配置文件中设置了进程和协程的相关配置。

    157.有用过Django REST framework吗?

    Django REST framework是一个强大而灵活的Web API工具。使用RESTframework的理由有:

    Web browsable API对开发者有极大的好处

    包括OAuth1a和OAuth2的认证策略

    支持ORM和非ORM数据资源的序列化

    全程自定义开发--如果不想使用更加强大的功能,可仅仅使用常规的function-based views额外的文档和强大的社区支持

    158.对cookies与session的了解?他们能单独用吗?

    Session采用的是在服务器端保持状态的方案,而Cookie采用的是在客户端保持状态的方案。但是禁用Cookie就不能得到Session。因为Session是用Session ID来确定当前对话所对应的服务器Session,而Session ID是通过Cookie来传递的,禁用Cookie相当于SessionID,也就得不到Session。

    爬虫

    159.试列出至少三种目前流行的大型数据库

    160.列举您使用过的Python网络爬虫所用到的网络数据包?

    requests, urllib,urllib2, httplib2

    161.爬取数据后使用哪个数据库存储数据的,为什么?

    162.你用过的爬虫框架或者模块有哪些?优缺点?

    Python自带:urllib,urllib2

    第三方:requests

    框架: Scrapy

    urllib 和urllib2模块都做与请求URL相关的操作,但他们提供不同的功能。

    urllib2: urllib2.urlopen可以接受一个Request对象或者url,(在接受Request对象时,并以此可以来设置一个URL的headers),urllib.urlopen只接收一个url。

    urllib 有urlencode,urllib2没有,因此总是urllib, urllib2常会一起使用的原因

    scrapy是封装起来的框架,他包含了下载器,解析器,日志及异常处理,基于多线程,twisted的方式处理,对于固定单个网站的爬取开发,有优势,但是对于多网站爬取100个网站,并发及分布式处理不够灵活,不便调整与扩展

    requests是一个HTTP库,它只是用来请求,它是一个强大的库,下载,解析全部自己处理,灵活性高

    Scrapy优点:异步,xpath,强大的统计和log系统,支持不同url。shell方便独立调试。写middleware方便过滤。通过管道存入数据库

    163.写爬虫是用多进程好?还是多线程好?

    164.常见的反爬虫和应对方法?

    165.解析网页的解析器使用最多的是哪几个?

    166.需要登录的网页,如何解决同时限制ip,cookie,session

    167.验证码的解决?

    168.使用最多的数据库,对他们的理解?

    169.编写过哪些爬虫中间件?

    170.“极验”滑动验证码如何破解?

    171.爬虫多久爬一次,爬下来的数据是怎么存储?

    172.cookie过期的处理问题?

    173.动态加载又对及时性要求很高怎么处理?

    174.HTTPS有什么优点和缺点?

    175.HTTPS是如何实现安全传输数据的?

    176.TTL,MSL,RTT各是什么?

    177.谈一谈你对Selenium和PhantomJS了解

    178.平常怎么使用代理的 ?

    179.存放在数据库(redis、mysql等)。

    180.怎么监控爬虫的状态?

    181.描述下scrapy框架运行的机制?

    182.谈谈你对Scrapy的理解?

    183.怎么样让 scrapy 框架发送一个 post 请求(具体写出来)

    184.怎么监控爬虫的状态 ?

    185.怎么判断网站是否更新?

    186.图片、视频爬取怎么绕过防盗连接

    187.你爬出来的数据量大概有多大?大概多长时间爬一次?

    188.用什么数据库存爬下来的数据?部署是你做的吗?怎么部署?

    189.增量爬取

    190.爬取下来的数据如何去重,说一下scrapy的具体的算法依据。

    191.Scrapy的优缺点?

    192.怎么设置爬取深度?

    193.scrapy和scrapy-redis有什么区别?为什么选择redis数据库?

    194.分布式爬虫主要解决什么问题?

    195.什么是分布式存储?

    196.你所知道的分布式爬虫方案有哪些?

    197.scrapy-redis,有做过其他的分布式爬虫吗?

    数据库

    MySQL

    198.主键 超键 候选键 外键

    主键:数据库表中对存储数据对象予以唯一和完整标识的数据列或属性的组合。一个数据列只能有一个主键,且主键的取值不能缺失,即不能为空值(Null).

    超键:在关系中能唯一标识元组的属性集称为关系模式的超键。一个属性可以作为一个超键,多个属性组合在一起也可以作为一个超键。超键包含候选键和主键。

    候选键:是最小超键,即没有冗余元素的超键。

    外键:在一个表中存在的另一个表的主键称此表的外键。

    199.视图的作用,视图可以更改么?

    视图是虚拟的表,与包含数据的表不一样,视图只包含使用时动态检索数据的查询;不包含任何列或数据。使用视图可以简化复杂的sql操作,隐藏具体的细节,保护数据;视图创建后,可以使用与表相同的方式利用它们。

    视图不能被索引,也不能有关联的触发器或默认值,如果视图本身内有order by则对视图再次order by将被覆盖。

    创建视图: create view xxx as xxxxxx

    对于某些视图比如未使用联结子查询分组聚集函数Distinct Union等,是可以对其更新的,对视图的更新将对基表进行更新;但是视图主要用于简化检索,保护数据,并不用于更新,而且大部分视图都不可以更新。

    200.drop,delete与truncate的区别

    drop直接删掉表,truncate删除表中数据,再插入时自增长id又从1开始,delete删除表中数据,可以加where字句。

    1.delete 语句执行删除的过程是每次从表中删除一行,并且同时将该行的删除操作作为事务记录在日志中保存以便进行回滚操作。truncate table则一次性地从表中删除所有的数据并不把单独的删除操作记录记入日志保存,删除行是不能恢复的。并且在删除的过程中不会激活与表有关的删除触发器,执行速度快。

    2.表和索引所占空间。当表被truncate后,这个表和索引所占用的空间会恢复到初始大小,而delete操作不会减少表或索引所占用的空间。drop语句将表所占用的空间全释放掉。

    3.一般而言,drop>truncate>delete

    4.应用范围。truncate只能对table,delete可以是table和view

    5.truncate和delete只删除数据,而drop则删除整个表(结构和数据)

    6.truncate与不带where的delete:只删除数据,而不删除表的结构(定义)drop语句将删除表的结构被依赖的约束(constrain),触发器(trigger)索引(index);依赖于该表的存储过程/函数将被保留,但其状态会变为:invalid.

    201.索引的工作原理及其种类

    数据库索引,是数据库管理系统中一个排序的数据结构,以协助快速查询,更新数据库表中数据。索引的实现通常使用B树以其变种B+树。

    在数据之外,数据库系统还维护着满足特定查找算法的数据结构,这些数据结构以某种方式引用(指向)数据,这样就可以在这些数据结构上实现高级查找算法。这种数据结构,就是索引。

    为表设置索引要付出代价的:一是增加了数据库的存储空间,二是在插入和修改数据时要花费较多的时间(因为索引也要随之变动)

    202.连接的种类

    203.数据库优化的思路

    204.存储过程与触发器的区别

    205.悲观锁和乐观锁是什么?

    206.你常用的mysql引擎有哪些?各引擎间有什么区别?

    Redis

    207.Redis宕机怎么解决?

    宕机:服务器停止服务‘

    如果只有一台redis,肯定 会造成数据丢失,无法挽救

    多台redis或者是redis集群,宕机则需要分为在主从模式下区分来看:

    slave从redis宕机,配置主从复制的时候才配置从的redis,从的会从主的redis中读取主的redis的操作日志1,在redis中从库重新启动后会自动加入到主从架构中,自动完成同步数据;

    2, 如果从数据库实现了持久化,此时千万不要立马重启服务,否则可能会造成数据丢失,正确的操作如下:在slave数据上执行SLAVEOF ON ONE,来断开主从关系并把slave升级为主库,此时重新启动主数据库,执行SLAVEOF,把它设置为从库,连接到主的redis上面做主从复制,自动备份数据。

    以上过程很容易配置错误,可以使用redis提供的哨兵机制来简化上面的操作。简单的方法:redis的哨兵(sentinel)的功能

    208.redis和mecached的区别,以及使用场景

    区别

    1、redis和Memcache都是将数据存放在内存中,都是内存数据库。不过memcache还可以用于缓存其他东西,例如图片,视频等等

    2、Redis不仅仅支持简单的k/v类型的数据,同时还提供list,set,hash等数据结构的存储

    3、虚拟内存-redis当物流内存用完时,可以将一些很久没用的value交换到磁盘

    4、过期策略-memcache在set时就指定,例如set key1 0 0 8,即永不过期。Redis可以通过例如expire设定,例如expire name 10

    5、分布式-设定memcache集群,利用magent做一主多从,redis可以做一主多从。都可以一主一丛

    6、存储数据安全-memcache挂掉后,数据没了,redis可以定期保存到磁盘(持久化)

    7、灾难恢复-memcache挂掉后,数据不可恢复,redis数据丢失后可以通过aof恢复

    8、Redis支持数据的备份,即master-slave模式的数据备份

    9、应用场景不一样,redis除了作为NoSQL数据库使用外,还能用做消息队列,数据堆栈和数据缓存等;Memcache适合于缓存SQL语句,数据集,用户临时性数据,延迟查询数据和session等

    使用场景

    1,如果有持久方面的需求或对数据类型和处理有要求的应该选择redis

    2,如果简单的key/value存储应该选择memcached.

    209.Redis集群方案该怎么做?都有哪些方案?

    1,codis

    目前用的最多的集群方案,基本和twemproxy一致的效果,但它支持在节点数量改变情况下,旧节点数据客恢复到新hash节点

    2redis cluster3.0自带的集群,特点在于他的分布式算法不是一致性hash,而是hash槽的概念,以及自身支持节点设置从节点。具体看官方介绍

    3.在业务代码层实现,起几个毫无关联的redis实例,在代码层,对key进行hash计算,然后去对应的redis实例操作数据。这种方式对hash层代码要求比较高,考虑部分包括,节点失效后的替代算法方案,数据震荡后的字典脚本恢复,实例的监控,等等

    210.Redis回收进程是如何工作的

    一个客户端运行了新的命令,添加了新的数据。

    redis检查内存使用情况,如果大于maxmemory的限制,则根据设定好的策略进行回收。

    一个新的命令被执行等等,所以我们不断地穿越内存限制的边界,通过不断达到边界然后不断回收回到边界以下。

    如果一个命令的结果导致大量内存被使用(例如很大的集合的交集保存到一个新的键),不用多久内存限制就会被这个内存使用量超越。

    MongoDB

    211.MongoDB中对多条记录做更新操作命令是什么?

    212.MongoDB如何才会拓展到多个shard里?

    测试

    213.编写测试计划的目的是

    214.对关键词触发模块进行测试

    215.其他常用笔试题目网址汇总

    216.测试人员在软件开发过程中的任务是什么

    217.一条软件Bug记录都包含了哪些内容?

    218.简述黑盒测试和白盒测试的优缺点

    219.请列出你所知道的软件测试种类,至少5项

    220.Alpha测试与Beta测试的区别是什么?

    221.举例说明什么是Bug?一个bug report应包含什么关键字?

    数据结构

    222.数组中出现次数超过一半的数字-Python版

    223.求100以内的质数

    224.无重复字符的最长子串-Python实现

    225.通过2个5/6升得水壶从池塘得到3升水

    226.什么是MD5加密,有什么特点?

    227.什么是对称加密和非对称加密

    228.冒泡排序的思想?

    229.快速排序的思想?

    230.如何判断单向链表中是否有环?

    231.你知道哪些排序算法(一般是通过问题考算法)

    232.斐波那契数列

    **数列定义: **

    f 0 = f 1 = 1 f n = f (n-1) + f (n-2)

    根据定义

    速度很慢,另外(暴栈注意!⚠️️) O(fibonacci n)

    def fibonacci(n):
        if n == 0 or n == 1:
            return 1
        return fibonacci(n - 1) + fibonacci(n - 2)

    线性时间的

    状态/循环

    def fibonacci(n):
       a, b = 1, 1
       for _ in range(n):
           a, b = b, a + b
       return a

    递归

    def fibonacci(n):
        def fib(n_, s):
            if n_ == 0:
                return s[0]
            a, b = s
            return fib(n_ - 1, (b, a + b))
        return fib(n, (1, 1))

    map(zipwith)

    def fibs():
        yield 1
        fibs_ = fibs()
        yield next(fibs_)
        fibs__ = fibs()
        for fib in map(lambad a, b: a + b, fibs_, fibs__):
            yield fib
            
            
    def fibonacci(n):
        fibs_ = fibs()
        for _ in range(n):
            next(fibs_)
        return next(fibs)

    做缓存

    def cache(fn):
        cached = {}
        def wrapper(*args):
            if args not in cached:
                cached[args] = fn(*args)
            return cached[args]
        wrapper.__name__ = fn.__name__
        return wrapper
    
    @cache
    def fib(n):
        if n < 2:
            return 1
        return fib(n-1) + fib(n-2)

    利用 funtools.lru_cache 做缓存

    from functools import lru_cache
    
    @lru_cache(maxsize=32)
    def fib(n):
        if n < 2:
            return 1
        return fib(n-1) + fib(n-2)

    Logarithmic

    矩阵

    import numpy as np
    def fibonacci(n):
        return (np.matrix([[0, 1], [1, 1]]) ** n)[1, 1]

    不是矩阵

    def fibonacci(n):
        def fib(n):
            if n == 0:
                return (1, 1)
            elif n == 1:
                return (1, 2)
            a, b = fib(n // 2 - 1)
            c = a + b
            if n % 2 == 0:
                return (a * a + b * b, c * c - a * a)
            return (c * c - a * a, b * b + c * c)
        return fib(n)[0]

    233.如何翻转一个单链表?

    class Node:
        def __init__(self,data=None,next=None):
            self.data = data
            self.next = next
            
    def rev(link):
        pre = link
        cur = link.next
        pre.next = None
        while cur:
            temp  = cur.next
            cur.next = pre
            pre = cur
            cur = tmp
        return pre
    
    if __name__ == '__main__':
        link = Node(1,Node(2,Node(3,Node(4,Node(5,Node(6,Node7,Node(8.Node(9))))))))
        root = rev(link)
        while root:
            print(roo.data)
            root = root.next

    234.青蛙跳台阶问题

    一只青蛙要跳上n层高的台阶,一次能跳一级,也可以跳两级,请问这只青蛙有多少种跳上这个n层台阶的方法?

    方法1:递归

    设青蛙跳上n级台阶有f(n)种方法,把这n种方法分为两大类,第一种最后一次跳了一级台阶,这类共有f(n-1)种,第二种最后一次跳了两级台阶,这种方法共有f(n-2)种,则得出递推公式f(n)=f(n-1) + f(n-2),显然f(1)=1,f(2)=2,这种方法虽然代码简单,但效率低,会超出时间上限

    class Solution:
        def climbStairs(self,n):
            if n ==1:
                return 1
            elif n==2:
                return 2
            else:
                return self.climbStairs(n-1) + self.climbStairs(n-2)

    方法2:用循环来代替递归

    class Solution:
        def climbStairs(self,n):
            if n==1 or n==2:
                return n
            a,b,c = 1,2,3
            for i in range(3,n+1):
                c = a+b
                a = b
                b = c
            return c

    235.两数之和 Two Sum

    236.搜索旋转排序数组 Search in Rotated Sorted Array

    237.Python实现一个Stack的数据结构

    238.写一个二分查找

    239.set 用 in 时间复杂度是多少,为什么?

    240.列表中有n个正整数范围在[0,1000],进行排序;

    241.面向对象编程中有组合和继承的方法实现新的类

    大数据

    242.找出1G的文件中高频词

    243.一个大约有一万行的文本文件统计高频词

    244.怎么在海量数据中找出重复次数最多的一个?

    245.判断数据是否在大量数据中

  • 相关阅读:
    project和task
    Gradle的安装
    Spring 集成 RMI
    RMI远程调用
    安装、启动与基本配置
    删除
    文件的分隔与合并
    my27_OGG MySQL To MySQL错误汇总
    1.5 GO json转Map
    1.4 Go语言-switch语句(转)
  • 原文地址:https://www.cnblogs.com/haixiaozh/p/13497108.html
Copyright © 2020-2023  润新知