python常见问题
Posted llbky
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了python常见问题相关的知识,希望对你有一定的参考价值。
1、 __new__.__init__区别,如何实现单例模式,有什么优点
__new__是一个静态方法,__init__是一个实例方法
__new__返回一个创建的实例,__init__什么都不返回
__new__返回一个cls的实例时后面的__init__才能被调用
当创建一个新实例时调用__new__,初始化一个实例时调用__init__
2、深浅拷贝
浅拷贝只是增加了一个指针指向一个存在的地址,而深拷贝是增加一个指针并且开辟了新的内存,这个增加的指针指向这个新的内存,采用浅拷贝的情况,释放内存,会释放同一内存,深拷贝就不会出现释放同一内存的错误
3、HTTP/IP相关协议,分别位于哪层
http协议是超文本传输协议,http协议是基于TCP/IP通信协议来传递数据
http协议工作与c/s架构上,浏览器作为http的客户端通过URL向http服务端即web服务器发送所用请求。web服务器收到所有请求后,向客户端发送响应信息,
http特点是短连接,无状态
地址栏键输入URL,按下回车之后经历了什么?
1.浏览器向DNS服务器请求解析该URL中的域名所对应的IP地址
2.解析出IP地址后,根据IP地址和默认端口80,和服务器建立TCP连接
3.浏览器发出读取文件的http请求,该请求报文作为TCP三次握手的第三个报文的数据发送给服务器
4.服务器对浏览器请求做出响应,并把对应的html文件发送给浏览器
5.释放TCP连接
6.浏览器将该HMTL渲染并显示内容
4、TCP/UDP区别
TCP协议是面向连接,保证高可靠性(数据无丢失,数据无失序,数据无错误,数据无重复达到)传输层协议
UDP:数据丢失,无秩序的传输层协议(qq基于udp协议)
5、webscoket
websocket是基于http协议的,可持续化连接
轮询:浏览器每隔几秒就发送一次请求,询问服务器是否有新消息
长轮询:客户端发起连接后,如果没有消息,就一直不返回response给客户端,直到有消息返回,返回完之后,客户端再次发起连接
6、RabbitMQ:
服务器端有Erlang语言来编写,支持多种客户端,只会ajax,用于分布式系统中存储转发消息,在易用性、扩展性、高可用性的方面不俗。
connection是RabbitMQ的socket连接,它封装了socket部分相关协议逻辑
connectionFactroy为connection的制造工厂
channel是我们与RabbitMQ打交道的最重要的一个接口,大部分的业务操作是在chaanel这个接口中完成,包括定义Queue、定义Exchange、
绑定Queue与Exchange,发布消息等
7、装饰器
调用装饰器其实是一个闭包函数,为其他函数添加附加功能,不修改被修改的源代码和不修改被修饰的方式,装饰器的返回值也是一个函数对象。
比如:插入日志、性能测试、事物处理、缓存、权限验证等,有了装饰器,就可以抽离出大量与函数功能本身无关的雷同代码并继续重用。
8、闭包
1.必须有一个内嵌函数
2.内嵌函数必须引用外部函数的变量(该函数包含对外作用域而不是全局作用域名字的引用)
3.外部函数的返回值必须是内嵌函数
9、迭代器与生成器
迭代器:迭代可迭代对象对应_iter_(方法)和迭代器对应_next_(方法)的一个过程
在后台 for 语句 对容器象调用 iter()函数。iter()是 python 的内置函数,iter() 会返回一个定义 next()方法的迭代器对象,它在容器中逐个访问容器内元素,next()也是 python 的内置函数。在没有后续元素时, next()会 抛出一个 StopIter 异常。
生成器:包括含有yield这个关键字,生成器也是迭代器,调动next把函数变成迭代器。
需要返回数据时候使用 yield 语 句。每次 next()被调用,生成器会返回它脱离的位置,记忆语句最 后一次执行和所有数据。
区别:生成器能做到迭代的所有事 ,而且因为自动创建了 __iter__()和 next()方法 ,生成器显得特别简洁 ,而且生成器也是 高效的 ,使用生成器表达式取代列解析可以同时节省内存。除了创建和保程序状态的自动方法,当发生器终结时 ,还会自动抛出 StopIteration 异常。
10、classmethod,staticmethod,property
类方法:将类的函数转换成类方法,函数上装饰@classmethod会将函数的自动传值参数改成cls
静态方法:此方法相当于给类扩展一个功能,将类内的函数实例化,给类或对象使用,此时类内的函数就是普通函数,不管是类还是实例化的对象都可以使用
实例化:类的实例化就会产生一个实例(对象),可以理解为类()把虚拟的东西实例化,得到具体存在的值
11、常用的状态码
200--服务器成功返回网页
204--请求收到,但返回信息为空
304--客户端已经执行了GET,但文件未变化
400--错误请求,如语法错误
403--无权限访问
404--请求的页面不存在
500--服务器产生内部错误
12、多进程,多线程,协程,GIL
GIL:全局解释器锁,是锁在cpython解释器上,导致同一时刻,同一进程只能有一个线程被执行
多进程:多进程模块multiprocessing来实现,cpu密集型,IO计算型可以用多进程
多线程:多线程模块threading来实现,IO密集型(IO:输入输出),多线程可以提高效率
GIL 对多线程的影响?
每个CPU在同一时间只能执行一个线程,在 Python 多线程下,每个线程的执行方式:
- 获取 GIL
- 执行代码直到 sleep 或者是 python 虚拟机将其挂起。
- 释放 GIL 可见,某个线程想要执行,必须先拿到 GIL,我们可以把 GIL 看 作是“通行证”,并且在一个 python 进程中,GIL 只有一个。拿不 到通行证的线程,就不允许进入 CPU 执行。 在 Python2.x 里,GIL 的释放逻辑是当前线程遇见 IO 操作或者 ticks 计数达到 100(ticks 可以看作是 Python 自身的一个计数器, 专门做用于 GIL,每次释放后归零,这个计数可以通过 sys.setcheckinterval 来调整),进行释放。而每次释放 GIL 锁, 线程进行锁竞争、切换线程,会消耗资源。并且由于 GIL 锁存在, python 里一个进程永远只能同时执行一个线程(拿到 GIL 的线程才能 执行)。 IO 密集型代码(文件处理、网络爬虫等),多线程能够有效提升 效率(单线程下有 IO 操作会进行 IO 等待,造成不必要的时间浪费, 而开启多线程能在线程 A 等待时,自动切换到线程 B,可以不浪费 CPU 的资源,从而能提升程序执行效率),所以多线程对 IO 密集型代码比 较友好。
并发:并发是指两个或多个事件在同一时间间隔内发生,同时可以容纳任务的极限
并行:并行是指两个或者多个事件在同一时刻发生,同时事件所能同时进行极限,比如6核可以真正同时进行6个进程
在单核 CPU 下的多线程其实都只是并发, 不是并行,并发和并行从宏观上来讲都是同时处理多路请求的概念。
协程:又称纤程,python在一个线程当中完成轮询。依赖于geenlet,对于多线程应用。协程是一种用户态的轻量级线程,调度完全由用户控制。 协程拥有自己的寄存器上下文和栈。协程调度切换时,将寄存器上下文和栈保存到其他地方,在切回来的时候,恢复先前保存的寄存器上下文和栈,直接操作栈则基本没有内核切换的开销,可以不加锁的访问全局变量,所以上下文的切换非常快。
进程:是资源管理单位,进程是相互独立的占用独立内存,是具有一定独立功能的程序关于某个数据集合上的一次运行活动,上下文进程间的切换开销(栈、 寄存器、虚拟内存、文件句柄等)比较大,但相对比较稳定安全。python可以同时开启多个进程。
线程:线程是进程的一个实体,是CPU调度的最小执行单位,线程的出现为了降低上下文切换的消耗,提供系统的并发性,不同进程通过进程间通信来通信。线程自己基本上不拥有系统 资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存 器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源。线程间通信主要通过共享内存,上下文切换很快,资源开 销较少,但相比进程不够稳定容易丢失数据。python一个进程只能同时开启一个线程,多个线程轮询。
python 的多进程与多线程的运行机制是什么?有什么区别? 分别在什么情况下用?
运行机制:进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是 系统进行资源分配和调度的一个独立单位 线程是进程的一个实体,是 CPU 调度和分派的基本单位,它是比进程更小的能独立 运行的基本单位.线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源 (如果制造数据的速度时快时慢,缓冲区的好处就体现出来了。当数据制造快的时候,消费者来不及处理,未处理的数据可以暂时存在缓冲区中。 程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有 的全部资源.
区别: 多进程稳定性好,一个子进程崩溃了,不会影响主进程以及其余进程。但是缺点是创 建进程的代价非常大,因为操作系统要给每个进程分配固定的资源,并且,操作系统对进 程的总数会有一定的限制,若进程过多,操作系统调度都会存在问题,会造成假死状态。 多线程效率较高一些,但是致命的缺点是任何一个线程崩溃都可能造成整个进程的崩 溃,因为它们共享了进程的内存资源池。
使用情况:如果代码是 IO 密集型——多线程。
如果代码是 CPU 密集型的——多进程是更好的选择,特别是计算密集型所使用的机器是多核或多CPU的。
13、IO多路复用/异步非阻塞
IO多路复用:通过一种机制,可以监听多个描述符 select/poll/epoll
select:连接数受限,查找配对速度慢,数据由内核拷贝到用户态
poll:改善了连接数,但是还是查找配对速度慢,数据由内核拷贝到用户态
epoll:epoll是linux下多路复用IO接口,是select/poll的增强版,它能显著提高程序在大量并发连接中只有少量活跃的情况下的系统CPU利用率
异步非阻塞:异步体现在回调上,回调就是有消息返回时告知一声儿进程进行处理。非阻塞就是不等待,不需要进程等待下去,
继续执行其他操作,不管其他进程的状态。
谈谈你对同步异步阻塞非阻塞理解?
同步:在发出一个功能调用时,在没有得到结果之前,该调用就不返回。绝大多数函数都是同步调用(例 如 sin, isdigit 等)。
异步:异步的概念和同步相对。当一个异步过程调用发出后,调用者不能立刻得到结果。实际处理这个调用的部件在完成后,通过状态、通知 和回调来通知调用者。
以 CAsycSocket 类为例(注意,CSocket 从 CAsyncSocket 派生,但是起功能已经由异步转化为同步),当一个 客户端通过调用 Connect 函数发出一个连接请求后,调用者线程立 刻可以朝下运行。当连接真正建立起来以后,socket 底层会发送一 个消息通知该对象。这里提到执行部件和调用者通过三种途径返回结 果:状态、通知和回调。可以使用哪一种依赖于执行部件的实现,除 非执行部件提供多种选择,否则不受调用者控制。如果执行部件用状 态来通知,那么调用者就需要每隔一定时间检查一次,效率就很低。如果是使用通知的方式,效率则很高, 因为执行部件几乎不需要做额外的操作。至于回调函数,其实和通知没太多区别。
阻塞调用:是指调用结果返回之前,当前线程会被挂起。函数只有在 得到结果之后才会返回。
实际上阻塞调用和同步调用是不同的。对于同步调用来说,很多时候当前线程还是激活的,只是从逻辑上当前函数没有返回而已。
例如,我们在 CSocket 中 调用 Receive 函数,如果缓冲区中没有数据,这个函数就会一直等待, 直到有数据才返回。而此时,当前线程还会继续处理各种各样的消息。 如果主窗口和调用函数在同一个线程中,除非你在特殊的界面操作函 数中调用,其实主界面还是应该可以刷新。socket 接收数据的另外 一个函数 recv 则是一个阻塞调用的例子。当 socket 工作在阻塞模式 的时候,如果没有数据的情况下调用该函数,则当前线程就会被挂起, 直到有数据为止。
非阻塞调用:非阻塞和阻塞的概念相对应,指在不能立刻得到结果之前,该函数不会阻塞当前线程,而会立刻返回。
对象的阻塞模式和阻塞函数调用:对象是否处于阻塞模式和函数是不是阻塞调用有很强的相关性,但是并不是一一对应的。阻塞对象上 可以有非阻塞的调用方式,我们可以通过一定的 API 去轮询状态,在适当的时候调用阻塞函数,就可以避免阻塞。而对于非阻塞对象,调 用特殊的函数也可以进入阻塞调用。函数 select 就是这样的一个例子。
14、PEP8规范是什么?
变量:
常量:大写加下划线 USER_CONSTANT 私有变量 : 小写和一个前导下划线 _private_value Python 中不存在私有变量一说,若是遇到需要保护的变量,使用小写和一个前导下划线。内置变量 : 小写,两个前导下划线和两个后置下划线 __class__ 两个前导下划线会导致变量在解释期间被更名。这是为了避免内置变量和 其他变量产生冲突。
函数和方法:
总体而言应该使用小写和下划线。但有些比较老的库使用的是混合大小写, 即首单词小写,之后每个单词第一个字母大写,其余小写。但现在,小写和下划 线已成为规范。 私有方法 :小写和一个前导下划线 def _secrete(self): print "don‘t test me." 这里和私有变量一样,并不是真正的私有访问权限。同时也应该注意一般函 数不要使用两个前导下划线(当遇到两个前导下划线时,Python 的名称改编特性 将发挥作用)。 特殊方法 :小写和两个前导下划线,两个后置下划线 def __add__(self, other): return int.__add__(other) 这种风格只应用于特殊函数,比如操作符重载等。 函数参数 : 小写和下划线,缺省值等号两边无空格
类:
类总是使用驼峰格式命名,即所有单词首字母大写其余字母小写。类名应该简明,精确,并足以从中理解类所完成的工作。常见的一个方法是使用表示其类 型或者特性的后缀,例如: SQLEngine,MimeTypes 对于基类而言,可以使用一个 Base 或者 Abstract 前 缀 BaseCookie,AbstractGroup
模块和包:
除特殊模块 __init__ 之外,模块名称都使用不带下划线的小写字母。 若是它们实现一个协议,那么通常使用 lib 为后缀,例如: import smtplib import os import sys
关于参数:
- 不要用断言来实现静态类型检测。断言可以用于检查参数,但不应仅仅是进 行静态类型检测。 Python 是动态类型语言,静态类型检测违背了其设计思想。断言应该用于避免函数不被毫无意义的调用。
- 不要滥用 *args 和 **kwargs。*args 和 **kwargs 参数可能会破坏函数的健壮性。它们使签名变得模糊,而且代码常常开始在不应该的地方构建小的参数解析器。
其他:
- 使用 has 或 is 前缀命名布尔元素 is_connect = True has_member = False
- 用复数形式命名序列 members = [‘user_1‘, ‘user_2‘]
- 用显式名称命名字典 person_address = ‘user_1‘:‘10 road WD‘, ‘user_2‘ : ‘20 street huafu‘
- 避免通用名称 诸如 list, dict, sequence 或者 element 这样的名称应该避免。
- 避免现有名称 诸如 os, sys 这种系统已经存在的名称应该避免。
- 一些数字 一行列数 : PEP 8 规定为 79 列。根据自己的情况,比如不要超过满屏时编辑 器的显示列数。 一个函数 : 不要超过 30 行代码, 即可显示在一个屏幕类,可以不使用垂直游 标即可看到整个函数。 一个类 : 不要超过 200 行代码,不要有超过 10 个方法。一个模块不要超 过 500 行。
- 验证脚本 可以安装一个 pep8 脚本用于验证你的代码风格是否符合 PEP8。
15、range-and-xrange
都在循环时使用,xrange内存性能更好,xrange用法与range完全相同,range一个生成list对象,xrange是生成器
16、with上下文机制原理
_enter_和_exit_,上下文管理协议,即with语句,为了让一个对象兼容with语句,必须在这个对象类中声明_enter_和_exit_方法,
使用with语句的目的就是把代码块放入with中执行,with结束后,自动完成清理工作,无须受到干预
17、经典类、新式类
经典类遵循:深度优先,python2中
新式类遵循:广度优先,Python3中
18、有没有一个工具可以帮助查找Python的bug和进行静态的代码分析?
PyChecker是一个Python代码的静态分析工具,它可以帮助查找Python代码的bug,会对代码的复杂度和格式提出警告,
Pylint是另外一个工具可以进行codingstandard检查
19、 Python是如何进行内存管理的
1.对象引用计数:
引用计数增加的情况:
来保持追踪内存中的对象,所有对象都用引用计数,一个对象分配一个新名称
将其放入一个容器中(列表,字典,元祖)
引用计数减少的情况:
使用del语句对对象别名显示的销毁
引用超出作用域或被重新赋值
sys.getrefcount()函数可以获得对象的当前引用计数
2.标记-清除机制
3.分代技术
20、什么是python?使用python有什么好处?
python是一种编程语言,它有对象、模块、线程、异常处理和自动内存管理。它简洁,简单、方便、容易扩展、有许多自带的数据结果,而且它开源
21、什么是pickling和unpickling?
Pickle模块读入任何python对象,将它们转换成字符串,然后使用dump函数将其转储到一个文件中——这个过程叫做pickling
反之从存储的字符串文件中提取原始python对象的过程,叫做unpickling
22、python是如何被解释的?
Python是一种解释性语言,它的源代码可以直接运行,Python解释器会将源代码转换成中间语言,之后再翻译成机器码再执行
23、数组和元组之间的区别是什么?
数组和元祖之间的区别:数组内容可以被修改,而元组内容是只读的,不可被修改的,另外元组可以被哈希,比如作为字典的key
24、参数按值传递和引用传递是怎么实现的?
python中的一切都是类,所有的变量都是一个对象的引用。引用的值是由函数确定的,因此无法被改变,但是如果一个对象是可以被修改的,你可以改动对象
25、Python都有哪些自带的数据结构?
Python自带的数据结构分为可变和不可变的:可变的有:数组、集合、字典,不可变的是:字符串、元组、整数
26、什么是python的命名空间?
在python中,所有的名字都存在于一个空间中,它们在改空间中存在和被操作——这就是命名空间,它就好像一个盒子,在每个变量名字都对应装着一个对象,当查询变量的时候,会从该盒子里面寻找相应的对象
27、python中的unittest是什么?
在python中,unittest是python中的单元测试框架,它拥有支持共享搭建、自动测试、在测试中暂停代码、将不同测试迭代成一组
28、*args与**kwargs
*args代表位置参数,它会接收任意多个参数并把这些参数作为元祖传递给函数。**kwargs代表的关键字参数,返回的是字典,位置参数一定要放在关键字前面
29、在Python中什么是slicing?
slicing是一种在有序的对象类型中(数组、元祖、字符串)节选某一段的语法
30、Python中的docstring是什么?
Python中文档字符串被称为docstring,它在Python中的作用是为函数、模块和类注释生成文档
31、os与sys区别:
os是模块负责程序与操作系统的交互,提供了访问操作系统底层的接口
sys模块是负责程序与python解释器的交互,提供了一系列的函数和变量,用于操控Python时运行的环境
32、实现一个单例模式
_new_()在 _init_()之前被调用,用于生成实例对象。利用这个方法和类的属性的特点可以实现设计模式的单例模式。
单例模式是指创建唯一对象,单例模式设计的类只能实例,实例化1个对象
class Singleton(object): __instance=None def __init__(self): pass def __new__(cls, *args, **kwargs): if Singleton.__instance is None: Singleton.__instance=object.__new__(cls,*args,**kwargs) return Singleton.__instance
33. 大数据的文件的读取:
- 读取大几 G 的大文件,可以利用生成器 generator
- 对可迭代对象 file,进行迭代遍历:for line in file,会 自动地使用缓冲 IO(buffered IO)以及内存管理,而不必担心任何 大文件的问题。
with open(‘filename‘) as file: for line in file: do_things(line)
34. python 中的反射是什么?
python中反射的核心本质其实就是利用字符串的形式去对象(模 块)中操作(查找/获取/删除/添加)成员,一种基于字符串的事件 驱动。
35.什么是线程安全?
线程安全是在多线程的环境下,能够保证多个线程同时执行时程序依旧运行正确, 而且要保证对于共享的数据可以由多个线程存取,但是同一时刻只能有一个线 程进行存取。多线程环境下解决资源竞争问题的办法是加锁来保证存取操作的唯一性。
36. 十个常用的 linux 命令?
Ls,help,cd ,more,clear,mkdir,pwd,rm,grep,find,mv,su,date…
37. find 和 grep?
grep 命令是一种强大的文本搜索工具,grep 搜索内容串可以是正则表达式, 允许对文本文件进行模式查找。如果找到匹配模式, grep 打印包含模式的所有行。
find 通常用来在特定的目录下搜索符合条件的文件,也可以用来搜索特定用户 属主的文件。
38.什么是面向对象编程?
面向对象编程是一种解决软件复用的设计和编程方法。这种方法把软件系统中 相近相似的操作逻辑和操作应用数据、状态,以类的型式描述出来,以对象实例的形式 在软件系统中复用,以达到提高软件开发效率的作用。
39. 面向对象有那些技术?
类(Class): 用来描述具有相同的属性和方法的对象的集合。它定义了该集合中每个 对象所共有的属性和方法。对象是类的实例。
类变量:类变量在整个实例化的对象中是公用的。类变量定义在类中且在函数体 之外。类变量通常不作为实例变量使用。
数据成员:类变量或者实例变量用于处理类及其实例对象的相关的数据。 方法重写:如果从父类继承的方法不能满足子类的需求,可以对其进行改写,这 个过程叫方法的覆盖(override),也称为方法的重写。
实例变量:定义在方法中的变量,只作用于当前实例的类。
继承:即一个派生类(derived class)继承基类(base class)的字段和方法。 继承也允许把一个派生类的对象作为一个基类对象对待。
实例化:创建一个类的实例,类的具体对象。
方法:类中定义的函数。
对象:通过类定义的数据结构实例。对象包括两个数据成员(类变量和实例变量) 和方法。
40..静态方法和类方法是什么?
静态方法:需要通过修饰器@staticmethod 来进行修饰,静态方法不需要多 定义参数。
类方法:类方法是类对象所拥有的方法,需要用修饰器@classmethod 来标识 其为类方法,对于类方法,第一个参数必须是类对象,一般以 cls 作为第一个参 数(也可以用其他名称的变量作为其第一个参数),能够通过实例对象和类对象 去访问。
41. 类属性、实例属性是什么?
类属性:定义在类里面但在函数外面的变量,是静态的。类对象所拥有的属性,它被所有类对象的实例对象所共有,在内存中只存在一个副本。对于公有的类属性,在类外可以通过类对象和实例对象访问。
实例属性:定义在__init__()方法里的变量就是实例属性,这些属性只有被创建时才会 被创建。 当类属性与实例属性同名时,一个实例访问这个属性时实例属性会覆盖类属性。
42. 如果你需要执行一个定时任务,你会怎么做?
Linux 的 Crontab 执行命令: sudo crontab -e 例:0 */1 * * * /usr/local/etc/rc.d/lighttpd restart 每一小时重启 apache
43. 线上服务可能因为种种原因导致挂掉怎么办? L
inux 下的后台进程管理利器 supervisor 每次文件修改后在 linux 执行:service supervisord restart
44. 如何提高 Python 的运行效率,请说出不少于 2 种提高运行效 率的方法?
使用生成器
关键代码使用外部功能包:Cython、Pylnlne、PyPy、Pyrex 针对循环的优化——尽量避免在循环中访问变量的属性
45. 介绍下“消费者”和“生产者”模型。
生产者-消费者模型是多线程同步的经典案例。此模型中生产者向缓冲区 push 数据,消费者从缓冲区中 pull 数据。 这个 Demo 中缓冲区用 python 实现的 Queue 来做,这个模块是线程安全的使开发者不 用再为队列增加额外的互斥锁. 信号处理的实现是这样的:
- 主线程接到一个 SIGTERM 的信号后先通知 Consumer 停止向缓冲区 push 数据并退 出
- Produer 将缓冲区中的数据消费完全后在退出
- 主线程退出
生产者消费者模型的优点:
- 解耦 假设生产者和消费者分别是两个类。如果让生产者直接调用消费者的某个方法,那么生产 者对于消费者就会产生依赖(也就是耦合)。将来如果消费者的代码发生变化,可能会 影响到生产者。而如果两者都依赖于某个缓冲区,两者之间不直接依赖,耦合也就相应降 低了。
- 支持并发 由于生产者与消费者是两个独立的并发体,他们之间是用缓冲区作为桥梁连接,生产者只 需要往缓冲区里丢数据,就可以继续生产下一个数据,而消费者只需要从缓冲区了拿数据 即可,这样就不会因为彼此的处理速度而发生阻塞。
- 支持忙闲不均 如果制造数据的速度时快时慢,缓冲区的好处就体现出来了。当数据制造快的时候,消费 者来不及处理,未处理的数据可以暂时存在缓冲区中。 等生产者的制造速度慢下来,消 费者再慢慢处理掉。
46. 爬虫在向数据库存数据开始和结束都会发一条消息,是 scrapy 哪 个模块实现的?
Item Pipeline scrapy 的信号处理使用的是 dispatch模块
47. 爬取下来的数据如何去重,说一下具体的算法依据?
- 通过 MD5 生成电子指纹来判断页面是否改变
- .nutch 去重。nutch 中 digest 是对采集的每一个网页内容的32 位哈希值,如果两个网页内容完全一样,它们的 digest 值肯定会 一样。
48. 写爬虫是用多进程好?还是多线程好? 为什么?
IO 密集型代码(文件处理、网络爬虫等),多线程能够有效 提升效率(单线程下有 IO 操作会进行 IO 等待,造成不必要的时间浪 费,而开启多线程能在线程 A 等待时,自动切换到线程 B,可以不浪 费 CPU 的资源,从而能提升程序执行效率)。在实际的数据采集过程中,既考虑网速和响应的问题,也需要考虑自身机器的硬件情况,来设置多进程或多线程。
49. 说一下 numpy 和 pandas 的区别?分别的应用场景?
Numpy 是的扩展包,纯数学。 Pandas 做 以矩阵为基础的数学计算模块。提供了 一套名为 DataFrame 的数据结构,比较契合统计分析中的表结构,并 且提供了计算接口,可用 Numpy 或其它方式进行计算。
50. 验证码如何处理?
- Scrapy 自带处理验证码
- 获取到验证码图片的 url,调用第三方付费借口破解验证码
51.分布式有哪些方案,哪一种最好?
celery、beanstalk,gearman
个人认为gearman比较好。原因主要有以下几点:
- 技术类型简单,维护成本低。
- 简单至上。能满足当前的技术需求即可="" (分布式任务处理、="" 异步同步任务同时支持、任务队列的持久化、维护部署简单)。
- 有成熟的使用案例。instagram="" 就是使用的="" 来完成="" 图片的处理的相关任务,有成功的经验,我们当然应该借鉴。
52. post和 get 区别?
get请求:请求的数据会附加在url之后,以?分割和传输数据,多个参数用&连接。url的编码格式采用的是ascii 编码,而不是uniclde,即是说所有的非字符都要编码之后再传输。
post请求:post请求会把请求的数据放置在http请求包的包体中。上面的item="bandsaw" 就是实际的传输数据。
因此,get请求的数据会暴露在地址栏中,而post请求则不会。
传输数据的大小在http规范中,没有对长度和传输的数据大小进行限制。但是在实际开发过程中,对于get,特定的浏览器和服务器对url长度有限制。因此,在使用请求时,传输数据会受到限制。对于post,由于不是地址栏传值,理论上是不会受限制的,但是实际上各个服务器会规定对form提交数据大小进行限制,apache、iis 都有各自的配置。
安全性post的安全性比get的高。
53.为什么要三次握手和四次挥手?
建立连接的过程是利用客户服务器模式,假设主机a="" 为客户端,主机 b为服务器端。
- tcp的三次握手过程:主机a向b发送连接请求;主机b对收到的主机a的报文段进行确认;主机a再次对主机b的确认进行确认。
- 采用三次握手是为了防止失效的连接请求报文段突然又传送到主机因而产生错误。
失效的连接请求报文段是指:主机a发出的连接请求没有收到主机b 的确认,于是经过一段时间后,主机a又重新向主机b发送连接请求,且建立成功,顺序完成数据传输。考虑这样一种特殊情况,主机a第一次发送的连接请求并没有丢失,而是因为网络节点导致延迟达到主机 b,主机b以为是主机a又发起的新连接,于是主机b同意连接,并向主机a发回确认,但是此时主机a根本不会理会,主机b就一直在等待主机a发送数据,导致主机b资源浪费。
为什么采用两次握手不行?
原因就是上面说的失效的连接请求的特殊情况,因此采用三次握手刚刚好,两次可能出现失效,四次甚至更多次则没必要,反而复杂了
54.多线程有哪些模块?
在python中可使用的多线程模块主要有两个,thread和threading模块。
55.分布式爬虫主要解决什么问题?
ip 带宽 cpu io
56.什么是url?
url,即统一资源定位符,也就是我们说的网址,统一资源定位符是对可以从互联网上得到的资源的位置和访问方法的一种简洁的表示,是互联网上标准资源的地址。互联网上的每个文件都有一个唯一的url,它包含的信息指出文件的位置以及浏览器应该怎么处理它。
57. 创建一个简单 tcp 服务器需要的流程?
- socket 创建一个套接字
- bind 绑定 ip 和 port
- listen 使套接字变为可以被动链接
- accept 等待客户端的链接 5.recv/send 接收发送数据
58. TTL,MSL,RTT是什么?
- MSL:报文最大生存时间,他是任何报文在网络上存在的最长时间,超过这个 时间报文将被丢弃。
- TTL:TTL 是 time to live 的缩写,中文可以译为“生存时间”,这个生存时间是由源主机设置初始值但不是存的具体时间,而是存储了一个 ip 数据报可以经过的最 大路由数,每经过一个处理他的路由器此值就减 1,当此值为 0 则数据报将被丢弃,同 时发送 ICMP 报文通知源主机。RFC 793 中规定 MSL 为 2 分钟,实际应用中常用的是30 秒,1 分钟和 2 分钟等。TTL 与 MSL 是有关系的但不是简单的相等的关系,MSL 要大于等于 TTL。
- RTT: RTT 是客户到服务器往返所花时间(round-trip time,简称 RTT), TCP 含有动态估算 RTT 的算法。TCP 还持续估算一个给定连接的 RTT,这是因为 RTT 受网络传输拥塞程序的变化而变化。
59. 常用的反爬虫措施?
- 添加代理
- 降低访问频率
- User-Agent
- 动态 HTML 数据加载
- 验证码处理
- Cookie
60. 关于 HTTP/HTTPS 的区别,分别应该在什么场合下使用?
- Hhttps 协议需要到 ca 申请证书,一般免费证书很少,需要交费。
- http 是超文本传输协议,信息是明文传输,https 则是具有安全性的 ssl 加密传输协议
- http 和 https 使用的是完全不同的连接方式用的端口也不一样,前者是 80,后者是 443。
- http 的连接很简单,是无状态的 HTTPS 协议是由 SSL+HTTP 协议构建的可进行加密传输、身份认证的网络协议 要比 http 协议安全
应用场合:http适合于对传输速度,安全性要求不是很高,且需要快速开发的应用。如 web 应用, 小的手机游戏等等. https:https 应该用于任何场景!
HTTPS优点和缺点:
优点:
- 使用 HTTPS 协议可认证用户和服务器,确保数据发送到正确的客户机和服务器;
- HTTPS 协议是由 SSL+HTTP 协议构建的可进行加密传输、身份认证的网络协议, 要比 http 协议安全,可防止数据在传输过程中不被窃取、改变,确保数据的完整性。
- HTTPS 是现行架构下最安全的解决方案,虽然不是绝对安全,但它大幅增加了中 间人攻击的成本
缺点:
- HTTPS 协议的加密范围也比较有限,在黑客攻击、拒绝服务攻击、服务器劫持等方 面几乎起不到什么作用
- HTTPS 协议还会影响缓存,增加数据开销和功耗,甚至已有安全措施也会受到影响 也会因此而受到影响。
- SSL 证书需要钱。功能越强大的证书费用越高。个人网站、小网站没有必要一般不会用。
- HTTPS 连接服务器端资源占用高很多,握手阶段比较费时对网站的相应速度有负面影响。
- HTTPS 连接缓存不如 HTTP 高效。
61. HTTPS 是如何实现安全传输数据的?
HTTPS 其实就是在 HTTP 跟 TCP 中间加多了一层加密层 TLS/SSL。SSL 是个加密套件, 负责对 HTTP 的数据进行加密。TLS 是 SSL 的升级版。现在提到 HTTPS,加密套件基本指的是TLS。原先是应用层将数据直接给到 TCP 进行传输,现在改成应用层将数据给到 TLS/SSL,将数据加密后,再给到 TCP 进行传输。
HTTPS 安全证书是怎么来的,如何申请,国内和国外有哪些第 三方机构提供安全证书认证?
国内:沃通(WoSign) 中国人民银行联合 12 家银行建立的金融 CFCA 中国电信认证中心(CTCA) 海关认证中心(SCCA) 国家外贸部 EDI 中心建立的国富安 CA 安全认证中心 SHECA(上海 CA)为首的 UCA 协卡认证体系
国外:StartSSL GlobalSign GoDaddy Symantec
62.桶排序(最快最简单的排序)
桶排序的基本思想是将一个数据表分割成许多 buckets,然后每 个 bucket 各自排序,或用不同的排序算法,或者递归的使用 bucket sort 算法。也是典型的 divide-and-conquer 分而治之的策略。它是 一个分布式的排序,介于 MSD 基数排序和 LSD 基数排序之间
def bucketSort(nums):
# 选择一个最大的数
max_num = max(nums)
# 创建一个元素全是 0 的列表, 当做桶
bucket = [0]*(max_num+1)
# 把所有元素放入桶中, 即把对应元素个数加一
for i in nums: bucket[i] += 1
# 存储排序好的元素
sort_nums = []
# 取出桶中的元素
for j in range(len(bucket)):
if bucket[j] != 0:
for y in range(bucket[j]):
sort_nums.append(j)
return sort_nums nums = [5,6,3,2,1,65,2,0,8,0]
print bucketSort(nums)
桶排序是稳定的,桶排序是常见排序里最快的一种, 大多数情况下比快排还要快,桶排序非常快,但是同时也非常耗空间,基本上是最耗空间的 一种排序算法
63. 排序算法的分析
排序算法的稳定性:如果在对象序列中有两个对象r[i]和r[j] , 它们的排序码 k[i]==k[j] 。如果排序前后,对象 r[i]和 r[j] 的相 对位置不变,则称排序算法是稳定的;否则排序算法是不稳定的。
时间开销:排序的时间开销可用算法执行中的数据比较次数与数据移动次数来衡量。 算法运行时间代价的大略估算一般都按平均情况进行估算。对于那些受对象排序码序列初始排列及对象个数影响较大的,需要按最好情况和最坏情况进行估算。
空间开销:算法执行时所需的附加存储。
64.Python垃圾回收机制
Python中的垃圾回收是以引用计数为主,标记-清除、分代收集为辅。引用计数的缺陷是循环引用的问题。
引用计数
原理:当一个对象的引用被创建或者复制时,对象的引用计数加1;当一个对象的引用被销毁时,对象的引用计数减1,当对象的引用计数减少为0时,就意味着对象已经再没有被使用了,可以将其内存释放掉。
使用gc模块垃圾回收:import gc模块,并且is_enable()=True才会启动自动垃圾回收。
标记-清除
原理:将集合中对象的引用计数复制一份副本,这个计数副本的作用是寻找root object集合(该集合中的对象是不能被回收的)。当成功寻找到root object集合之后,首先将现在的内存链表一分为二,一条链表中维护root object集合,成为root链表,而另外一条链表中维护剩下的对象,成为unreachable链表。一旦在标记的过程中,发现现在的unreachable可能存在被root链表中直接或间接引用的对象,就将其从unreachable链表中移到root链表中;当完成标记后,unreachable链表中剩下的所有对象就是名副其实的垃圾对象了,接下来的垃圾回收只需限制在unreachable链表中即可。
分代
原理:将系统中的所有内存块根据其存活时间划分为不同的集合,每一个集合就成为一个“代”,垃圾收集的频率随着“代”的存活时间的增大而减小。也就是说,活得越长的对象,就越不可能是垃圾,就应该减少对它的垃圾收集频率。那么如何来衡量这个存活时间:通常是利用几次垃圾收集动作来衡量,如果一个对象经过的垃圾收集次数越多,可以得出:该对象存活时间就越长。
有三种情况会触发垃圾回收:
- 调用
gc.collect()
, - 当gc模块的计数器达到阀值的时候。
- 程序退出的时候
常用gc模块函数
- gc.set_debug(flags)
设置gc的debug日志,一般设置为gc.DEBUG_LEAK - gc.collect([generation])
显式进行垃圾回收,可以输入参数,0代表只检查第一代的对象,1代表检查一,二代的对象,2代表检查一,二,三代的对象,如果不传参数,执行一个full collection,也就是等于传2。
返回不可达(unreachable objects)对象的数目 - gc.set_threshold(threshold0[, threshold1[, threshold2])
设置自动执行垃圾回收的频率。 - gc.get_count()
获取当前自动执行垃圾回收的计数器,返回一个长度为3的列表
65.Python解决高并发思路
- HTML页面静态化
- 图片服务器分离(可以用fastdfs轻量级的分布式文件存储系统)
- 使用缓存(用redis)
- 数据库集群
- 库表散列使用负载均衡的方法(配置nigix服务器)
- 镜像
- CDN加速技术(内容分发网络)
66.with关键字:
- with语句时用于对try except finally 的优化,让代码更加美观,例如常用的开发文件的操作,用try except finally 实现,打开文件的时候,为了能正常释放文件的句柄,都要加个try,然后再finally里把f close掉,但是这样的代码不美观,finally就像个尾巴,一直托在后面,尤其是当try里面的语句时几十行。用with的实现,这条语句就好简洁很多,当with里面的语句产生异常的话,也会正常关闭文件。
- 除了打开文件,with只适用于上下文管理器的调用,除了文件外,with还支持 threading、decimal等模块,当然我们也可以自己定义可以给with调用的上下文管理器,使用类定义上下文管理器需要在类上定义__enter__和__exit__方法,执行with A() as a: 语句时会先执行__enter__方法,这个方法的返回值会赋值给后面的a变量,当with里面的语句产生异常或正常执行完时,都好调用类中的__exit__方法。
在开发的过程中,会有很多对象在使用之后,是需要执行一条或多条语句来进行关闭,释放等操作的,例如上面说的的文件,还有数据库连接,锁的获取等,这些收尾的操作会让代码显得累赘,也会造成由于程序异常跳出后,没有执行到这些收尾操作,而导致一些系统的异常,还有就是很多程序员会忘记写上这些操作-_-!-_-!,为了避免这些错误的产生,with语句就被生产出来了。with语句的作用就是让程序员不用写这些收尾的代码,并且即使程序异常也会执行到这些代码(finally的作用)
67.子类重写了`__init__`方法, 初始化子类时, 父类的`__init__`方法还会执行吗?
不会了。
如何在子类重写了`__init__`方法的情况下, 还能执行父类的`__init__`方法?
使用super方法。
68.python垃圾回收机制?
python有自动垃圾回收机制(当对象的引用计数为0时,解释器自动释放内存),出现内存泄露的场景一般是扩展库内存泄漏或循环引用,全局容器的对象没有删除。
避免方法:尽早释放无用对象的引用,少用静态变量,不经常在调用的方法中创建对象,尤其是不要再循环中创建对象。
69.is 和 == 有什么区别?
== 比较值是否相同 is比较内存单元,所以is比==更严格
以上是关于python常见问题的主要内容,如果未能解决你的问题,请参考以下文章