博派通达-Python爬虫开发工程师

1. 解释什么是栈溢出,在什么情况下可能出现。

栈溢出是由于C语言系列没有内置检查机制来确保复制到缓冲区的数据不得大于缓冲区的大小,
因此当这个数据足够大的时候,将会溢出缓冲区的范围。
在Python中,函数调用是通过栈(stack)这种数据结构实现的,
每当进入一个函数调用,栈就会加一层栈帧,每当函数返回,栈就会减一层栈帧。
由于栈的大小不是无限的,所以,递归调用的次数过多,会导致栈溢出。
栈溢出的几种情况:
1. 局部数组过大,当函数内部的数组过大时,有可能导致堆栈溢出。
2. 递归调用层次太多。递归函数在运行时会执行压栈操作,当压栈次数太多时,也会导致堆栈溢出。
3. 指针或数组越界。这种情况最常见,例如进行字符串拷贝,或处理用户输入等等。

2. 简述CPython的内存管理机制。

  • 垃圾回收:python不像C++,Java等语言一样,他们可以不用事先声明变量类型而直接对变量进行赋值。对Python语言来讲,对象的类型和内存都是在运行时确定的。这也是为什么我们称Python语言为动态类型的原因(这里我们把动态类型可以简单的归结为对变量内存地址的分配是在运行时自动判断变量类型并对变量进行赋值)。
  • 引用计数:Python采用了类似Windows内核对象一样的方式来对内存进行管理。每一个对象,都维护这一个对指向该对对象的引用的计数。当变量被绑定在一个对象上的时候,该变量的引用计数就是1,(还有另外一些情况也会导致变量引用计数的增加),系统会自动维护这些标签,并定时扫描,当某标签的引用计数变为0的时候,该对就会被回收。
  • 内存池机制Python的内存机制以金字塔行,1,2层主要有操作系统进行操作
    • 第0层是C中的malloc,free等内存分配和释放函数进行操作
    • 第1层和第2层是内存池,有Python的接口函数PyMem_Malloc函数实现,当对象小于256K时有该层直接分配内存
    • 第3层是最上层,也就是我们对Python对象的直接操作
  • 在 C 中如果频繁的调用malloc 与 free 时,是会产生性能问题的.再加上频繁的分配与释放小块的内存会产生内存碎片. Python 在这里主要干的工作有:
    • 如果请求分配的内存在1~256字节之间就使用自己的内存管理系统,否则直接使用 malloc
    • 这里还是会调用 malloc 分配内存,但每次会分配一块大小为256k的大块内存.
    • 经由内存池登记的内存到最后还是会回收到内存池,并不会调用 C 的 free 释放掉.以便下次使用.对于简单的Python对象,例如数值、字符串,元组(tuple不允许被更改)采用的是复制的方式(深拷贝?),也就是说当将另一个变量B赋值给变量A时,虽然A和B的内存空间仍然相同,但当A的值发生变化时,会重新给A分配空间,A和B的地址变得不再相同

3. 请列举你知道的Python的魔法方法及用途。

3.1 __init__

类的初始化方法。它获取任何传给构造器的参数(比如我们调用 x = SomeClass(10, ‘foo’) , __init__就会接到参数 10 和 ‘foo’ 。 __init__在Python的类定义中用的最多。

3.2 __new__:

__new__是对象实例化时第一个调用的方法,它只取下 cls 参数,并把其他参数传给 __init____new__很少使用,但是也有它适合的场景,尤其是当类继承自一个像元组或者字符串这样不经常改变的类型的时候.

3.3 __del__:

__new____init__是对象的构造器, __del__是对象的销毁器。它并非实现了语句 del x (因此该语句不等同于 x.__del__())。而是定义了当对象被垃圾回收时的行为。 当对象需要在销毁时做一些处理的时候这个方法很有用,比如 socket 对象、文件对象。但是需要注意的是,当Python解释器退出但对象仍然存活的时候,__del__并不会 执行。 所以养成一个手工清理的好习惯是很重要的,比如及时关闭连接。

4. 已知以下list:

list1 = {
    {
        "mm": 2,
    },{
        "mm": 1,
    },{
        "mm": 4,
    },{
        "mm": 3,
    },{
        "mm": 3,
    }
}
  • 4.1 把list1中的元素按mm的值排序。
  • 4.2 获取list1中第一个mm值等于x的元素。
  • 4.3 删除list1中所有mm等于x的元素,且不对list重新赋值。
  • 4.4 取出list1中mm最大的元素,不能排序。

5. 以下操作的时间复杂度是多少?

  • 5.1 list.index 时间复杂度:O(1)
  • 5.2 dict.get 时间复杂度:平均O(1), 最坏:O(n)
  • 5.3 x in set(…..) 时间复杂度:平均O(1), 最坏:O(n)

6. 解释以下输出的原因:

In [1]: '{:0.2}'.format(0.135)
Out[1]: '0.14'

In [2]: '{:0.2}'.format(0.145)
Out[2]: '0.14'

7. 简述代码跑出以下异常的原因是什么:

  • IndexError 序列中没有此索引(index)
  • AttributeError 对象没有这个属性
  • AssertionError 断言语句失败
  • NotImplementedError 尚未实现的方法
  • StopIteration 迭代器没有更多的值
  • TypeError 对类型无效的操作
  • IndentationError 缩进错误

8. 简述你对GIL的理解

GIL的全称是Global Interpreter Lock(全局解释器锁),来源是python设计之初的考虑,为了数据安全所做的决定。
每个CPU在同一时间只能执行一个线程
在单核CPU下的多线程其实都只是并发,不是并行,并发和并行从宏观上来讲都是同时处理多路请求的概念。
但并发和并行又有区别,并行是指两个或者多个事件在同一时刻发生;而并发是指两个或多个事件在同一时间间隔内发生。

在Python多线程下,每个线程的执行方式:
1、获取GIL    
2、执行代码直到sleep或者是python虚拟机将其挂起。
3、释放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密集型代码比较友好。

9. 简述以下内置函数的用法

9.1 (py2)reduce (py3 from functools import reduce)
reduce() 函数会对参数序列中元素进行累积。
函数将一个数据集合(链表,元组等)中的所有数据进行下列操作:
用传给reduce中的函数 function(有两个参数)先对集合中的第 12 个元素进行操作,
得到的结果再与第三个数据用 function 函数运算,最后得到一个结果。

reduce()函数语法:

reduce(function, iterable[, initializer])

参数:
- function 函数,有两个参数
- iterable 可迭代对象。
- initializer: 可选,初始参数。

返回值:
- 返回函数计算结果。

实例:

In [1]: def add(x,y):
   ...:     return x + y
   ...: 

In [2]: from functools import reduce

In [3]: reduce(add, [1,2,3,4,5])
Out[3]: 15

In [4]: reduce(lambda x, y: x + y, [1,2,3,4,5])
Out[4]: 15
9.2 map
map() 会根据提供的函数对指定序列做映射。
第一个参数 function 以参数序列中的每一个元素调用 function 函数,
返回包含每次 function 函数返回值的新列表。

语法:

map(function, iterable, ...)

参数:
- function 函数,有两个参数
- iterable 一个或多个序列

返回值:
- Python2 返回列表
- Python3 返回迭代器。

例子:

In [5]: def square(x):
   ...:     return x ** 2
   ...: 

In [6]: map(square, [1,2,3,4,5])
Out[6]: <map at 0x7fa1b0fde2e8>
9.3 any
any() 函数用于判断给定的可迭代参数 iterable 是否全部为空对象,
如果都为空、0false,则返回 False,
如果不都为空、0false,则返回 True

函数等价于:

def any(iterable):
    for element in iterable:
        if element:
            return True
    return False

语法:

any(iterable)

参数:
- iterable 元组或列表

返回值:

如果都为空、0false,则返回false,如果不都为空、0false,则返回true

例子:

In [8]: any(["a","b","c","d"])     # 列表list,元素都不为空或0
Out[8]: True

In [9]: any(['a', 'b', '', 'd'])     # 列表list,存在一个为空的元素
Out[9]: True

In [10]: any([0, '', False])    # 列表list,元素全为0,'',false
Out[10]: False

In [11]: any(('a', 'b', 'c', 'd'))    # 元组tuple,元素都不为空或0
Out[11]: True

In [12]: any(('a', 'b', '', 'd'))    # 元组tuple,存在一个为空的元素
Out[12]: True

In [13]: any((0, '', False))      # 元组tuple,元素全为0,'',false
Out[13]: False

In [14]: any([]) # 空列表
Out[14]: False

In [15]: any(()) # 空元组
Out[15]: False
9.4 all
all() 函数用于判断给定的可迭代参数 iterable 中的所有元素是否不为 0''False 或者 iterable 为空,
如果是返回 True,否则返回 False

函数等价于:

def all(iterable):
    for element in iterable:
        if not element:
            return False
    return True

语法:

all(iterable)

参数:
- iterable 元组或列表

返回值:

如果iterable的所有元素不为0''False或者iterable为空,all(iterable)返回True,否则返回False;
注意:空元组、空列表返回值为True,这里要特别注意。

例子:

In [21]: all(['a', 'b', 'c', 'd'])  # 列表list,元素都不为空或0
Out[21]: True

In [22]: all(['a', 'b', '', 'd'])   # 列表list,存在一个为空的元素
Out[22]: False

In [23]: all([0, 1, 2, 3])          # 列表list,存在一个为0的元素
Out[23]: False

In [24]: all(('a', 'b', 'c', 'd'))  # 元组tuple,元素都不为空或0
Out[24]: True

In [25]: all(('a', 'b', '', 'd'))   # 元组tuple,存在一个为空的元素
Out[25]: False

In [27]: all((0, 1, 2, 3))          # 元组tuple,存在一个为0的元素
Out[27]: False

In [28]: all([])             # 空列表
Out[28]: True

In [29]: all(())             # 空元组
Out[29]: True

10. copy和deepcopy的区别是什么?

copy.copy()浅拷贝,只拷贝父对象,不会拷贝对象的内部的子对象。
copy.deepcopy()深拷贝,拷贝对象及其子对象。

11. 简述多线程、多进程、协程之间的区别与联系。

概念:

  • 进程:

    进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,
    进程是系统进行资源分配和调度的一个独立单位。每个进程都有自己的独立内存空间,
    不同进程通过进程间通信来通信。由于进程比较重量,占据独立的内存,
    所以上下文进程间的切换开销(栈、寄存器、虚拟内存、文件句柄等)比较大,但相对比较稳定安全。
  • 线程:

    线程是进程的一个实体,是CPU调度和分派的基本单位,
    它是比进程更小的能独立运行的基本单位.
    线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),
    但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源。
    线程间通信主要通过共享内存,上下文切换很快,资源开销较少,但相比进程不够稳定容易丢失数据。
  • 协程:

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

区别:
进程与线程比较:

线程是指进程内的一个执行单元,也是进程内的可调度实体。线程与进程的区别:
1) 地址空间:线程是进程内的一个执行单元,进程内至少有一个线程,它们共享进程的地址空间,
而进程有自己独立的地址空间

2) 资源拥有:进程是资源分配和拥有的单位,同一个进程内的线程共享进程的资源

3) 线程是处理器调度的基本单位,但进程不是

4) 二者均可并发执行

5) 每个独立的线程有一个程序运行的入口、顺序执行序列和程序的出口,
但是线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制

协程与线程进行比较:

1) 一个线程可以多个协程,一个进程也可以单独拥有多个协程,这样python中则能使用多核CPU。

2) 线程进程都是同步机制,而协程则是异步

3) 协程能保留上一次调用时的状态,每次过程重入时,就相当于进入上一次调用的状态

12. 代码中经常遇到的*args, **kwargs含义及用法。

  • 在函数定义中使用*args**kwargs传递可变长参数
  • *args用来将参数打包成tuple给函数体调用
  • **kwargs 打包关键字参数成dict给函数体调用

13. 列举一些你知道的HTTP Header 及其功能。

Requests部分

Header 解释 示例
Accept 指定客户端能够接收的内容类型 Accept: text/plain, text/html
Accept-Charset 浏览器可以接受的字符编码集。 Accept-Charset: iso-8859-5
Accept-Encoding 指定浏览器可以支持的web服务器返回内容压缩编码类型。 Accept-Encoding: compress, gzip
Accept-Language 浏览器可接受的语言 Accept-Language: en,zh
Accept-Ranges 可以请求网页实体的一个或者多个子范围字段 Accept-Ranges: bytes
Authorization HTTP授权的授权证书 Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==
Cache-Control 指定请求和响应遵循的缓存机制 Cache-Control: no-cache
Connection 表示是否需要持久连接。(HTTP 1.1默认进行持久连接) Connection: close
Cookie HTTP请求发送时,会把保存在该请求域名下的所有cookie值一起发送给web服务器。 Cookie: $Version=1; Skin=new;
Content-Length 请求的内容长度 Content-Length: 348
Content-Type 请求的与实体对应的MIME信息 Content-Type: application/x-www-form-urlencoded
Date 请求发送的日期和时间 Date: Tue, 15 Nov 2010 08:12:31 GMT
Expect 请求的特定的服务器行为 Expect: 100-continue
From 发出请求的用户的Email From: [email protected]
Host 指定请求的服务器的域名和端口号 Host: www.zcmhi.com
If-Match 只有请求内容与实体相匹配才有效 If-Match: “737060cd8c284d8af7ad3082f209582d”
If-Modified-Since 如果请求的部分在指定时间之后被修改则请求成功,未被修改则返回304代码 If-Modified-Since: Sat, 29 Oct 2010 19:43:31 GMT
If-None-Match 如果内容未改变返回304代码,参数为服务器先前发送的Etag,与服务器回应的Etag比较判断是否改变 If-None-Match: “737060cd8c284d8af7ad3082f209582d”
If-Range 如果实体未改变,服务器发送客户端丢失的部分,否则发送整个实体。参数也为Etag If-Range: “737060cd8c284d8af7ad3082f209582d”
If-Unmodified-Since 只在实体在指定时间之后未被修改才请求成功 If-Unmodified-Since: Sat, 29 Oct 2010 19:43:31 GMT
Max-Forwards 限制信息通过代理和网关传送的时间 Max-Forwards: 10
Pragma 用来包含实现特定的指令 Pragma: no-cache
Proxy-Authorization 连接到代理的授权证书 Proxy-Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==
Range 只请求实体的一部分,指定范围 Range: bytes=500-999
Referer 先前网页的地址,当前请求网页紧随其后,即来路 Referer: http://zuoandroid.github.io
TE 客户端愿意接受的传输编码,并通知服务器接受接受尾加头信息 TE: trailers,deflate;q=0.5
Upgrade 向服务器指定某种传输协议以便服务器进行转换(如果支持) Upgrade: HTTP/2.0, SHTTP/1.3, IRC/6.9, RTA/x11
User-Agent User-Agent的内容包含发出请求的用户信息 User-Agent: Mozilla/5.0 (Linux; X11)
Via 通知中间网关或代理服务器地址,通信协议 Via: 1.0 fred, 1.1 nowhere.com (Apache/1.1)
Warning 关于消息实体的警告信息 Warn: 199 Miscellaneous warning

Responses 部分

Header 解释 示例
Accept-Ranges 表明服务器是否支持指定范围请求及哪种类型的分段请求 Accept-Ranges: bytes
Age 从原始服务器到代理缓存形成的估算时间(以秒计,非负) Age: 12
Allow 对某网络资源的有效的请求行为,不允许则返回405 Allow: GET, HEAD
Cache-Control 告诉所有的缓存机制是否可以缓存及哪种类型 Cache-Control: no-cache
Content-Encoding web服务器支持的返回内容压缩编码类型。 Content-Encoding: gzip
Content-Language 响应体的语言 Content-Language: en,zh
Content-Length 响应体的长度 Content-Length: 348
Content-Location 请求资源可替代的备用的另一地址 Content-Location: /index.htm
Content-MD5 返回资源的MD5校验值 Content-MD5: Q2hlY2sgSW50ZWdyaXR5IQ==
Content-Range 在整个返回体中本部分的字节位置 Content-Range: bytes 21010-47021/47022
Content-Type 返回内容的MIME类型 Content-Type: text/html; charset=utf-8
Date 原始服务器消息发出的时间 Date: Tue, 15 Nov 2010 08:12:31 GMT
ETag 请求变量的实体标签的当前值 ETag: “737060cd8c284d8af7ad3082f209582d”
Expires 响应过期的日期和时间 Expires: Thu, 01 Dec 2010 16:00:00 GMT
Last-Modified 请求资源的最后修改时间 Last-Modified: Tue, 15 Nov 2010 12:45:26 GMT
Location 用来重定向接收方到非请求URL的位置来完成请求或标识新的资源 Location: http://http://zuoandroid.github.io
Pragma 包括实现特定的指令,它可应用到响应链上的任何接收方 Pragma: no-cache
Proxy-Authenticate 它指出认证方案和可应用到代理的该URL上的参数 Proxy-Authenticate: Basic
refresh 应用于重定向或一个新的资源被创造,在5秒之后重定向(由网景提出,被大部分浏览器支持) Refresh: 5; url=http://http://zuoandroid.github.io
Retry-After 如果实体暂时不可取,通知客户端在指定时间之后再次尝试 Retry-After: 120
Server web服务器软件名称 Server: Apache/1.3.27 (Unix) (Red-Hat/Linux)
Set-Cookie 设置Http Cookie Set-Cookie: UserID=JohnDoe; Max-Age=3600; Version=1
Trailer 指出头域在分块传输编码的尾部存在 Trailer: Max-Forwards
Transfer-Encoding 文件传输编码 Transfer-Encoding:chunked
Vary 告诉下游代理是使用缓存响应还是从原始服务器请求 Vary: *
Via 告知代理客户端响应是通过哪里发送的 Via: 1.0 fred, 1.1 nowhere.com (Apache/1.1)
Warning 警告实体可能存在的问题 Warning: 199 Miscellaneous warning
WWW-Authenticate 表明客户端请求实体应该使用的授权方案 WWW-Authenticate: Basic

14. 简述Cookie和Session的区别与联系。

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

15. 简述什么是浏览器的同源策略。

同源策略是浏览器上为安全性考虑实施的非常重要的安全策略。
何谓同源:
URL由协议、域名、端口和路径组成,如果两个URL的协议、域名和端口相同,则表示他们同源。
同源策略:
浏览器的同源策略,限制了来自不同源的"document"或脚本,对当前"document"读取或设置某些属性。
从一个域上加载的脚本不允许访问另外一个域的文档属性。
举个例子:
比如一个恶意网站的页面通过iframe嵌入了银行的登录页面(二者不同源),如果没有同源限制,恶意网页上的javascript脚本就可以在用户登录银行的时候获取用户名和密码。
在浏览器中,<script>、<img>、<iframe>、<link>等标签都可以加载跨域资源,
而不受同源限制,但浏览器限制了JavaScript的权限使其不能读、写加载的内容。
另外同源策略只对网页的HTML文档做了限制,对加载的其他静态资源如javascript、css、图片等仍然认为属于同源。

16. git commit --amend 有和用处

git commit --amend命令是修复最新提交的便捷方式。它允许你将缓存的修改和之前的提交合并到一起,而不是提交一个全新的快照。它还可以用来简单地编辑上一次提交的信息而不改变快照。

但是,amend不只是修改了最新的提交——它进行了一次替换。对于Git来说,这看上去像一个全新的提交,即上图中用星号表示的那一个。在公共仓库工作时一定要牢记这一点。

17. git如何查看某次提交修改的内容

  • 首先我们可以使用git log 显示历史的提交列表
  • git show <commit-hashId> 便可以显示某次提交的修改内容
  • git show <commit-hashId> filename 可以显示某次提交的某个内容的修改信息。
  • git log -p <filename>查看某个文件的修改历史
  • git log -p -2 查看最近2次的更新内容

18. git如何比较两个commit的区别?

git diff commit-id-1 commit-id-2

19. git如何把分支A上某个commit应用到分支B上。

场景A分支上部分文件需要合并到B分支,然而这些文件又是多次commit,并不能直接使用cherry-pick。 
然而需要合并的文件并不是太多,所以果断的选择了merge的部分文件合并。 
1 首先切换到B分支 , git checkout branchB 
2 整理好需要合并的文件列表, git checkout branchA file1 file2 ……

20. 如何查看Linux系统的启动时间,磁盘使用量,内存使用量。

  • 查看启动时间: uptime
  • 查看磁盘使用情况:df -lh
文件系统           1K-块     已用     可用 已用% 挂载点
udev             3995164        0  3995164    0% /dev
tmpfs             803648     1588   802060    1% /run
/dev/sda9       90302304 27721560 57950628   33% /
tmpfs            4018224    85508  3932716    3% /dev/shm
tmpfs               5120        4     5116    1% /run/lock
tmpfs            4018224        0  4018224    0% /sys/fs/cgroup
tmpfs             803644       52   803592    1% /run/user/1000
/dev/sdb1       30293152  8970784 21322368   30% /media/wenbin/DEEPINOS
/dev/sda7       83006460 34375880 48630580   42% /media/wenbin/娱乐
/dev/sda6      105623056 11487268 94135788   11% /media/wenbin/文档
/dev/sda5      104858620 30341620 74517000   29% /media/wenbin/软件
/dev/sda1       94372860 46579172 47793688   50% /media/wenbin/287464E27464B46A
  • 查看内存使用量:top

       

猜你喜欢

转载自blog.csdn.net/PythonCodeZ/article/details/80496267