深圳骑士计划脱产班全栈3期第三次统一考试
考试时长:5个小时 满分:150分
评分标准(150分): (150, ‘A+‘), (136, ‘A‘), (129, ‘B+‘), (121, ‘B‘), (106, ‘B-‘), (91, ‘C+‘), (76, ‘C‘), (61, ‘C-‘), (0, ‘ D‘)
1.请编写一个函数实现将IP地址转换成一个整数。(5分)
如 10.3.9.12 转换规则为:
10 00001010
3 00000011
9 00001001
12 00001100
再将以上二进制拼接起来计算十进制结果:00001010 00000011 00001001 00001100 = ?
答:
# -*- coding: utf-8 -*- # __author: Tiger_Lee # @file: 考试题.py # @time: 2019 02 26 # @email: lxh661314@163.com # 赠送内容:用Python获取本机ip地址 from socket import gethostbyname_ex, gethostname local_IP_list = gethostbyname_ex(gethostname()) local_IP = gethostbyname_ex(gethostname())[2][0] def decimal_to_binary(ip_add): """ 将ip地址转换为二进制 :param ip_add: :return: """ num_list = ip_add.split(".") ip_binary = "" for num in num_list: num = bin(int(num))[2:] binary_num = num.rjust(8, ‘0‘) ip_binary += binary_num + " " return ip_binary[:-1] binary_ip = decimal_to_binary(local_IP) print(binary_ip) def binary_to_decimal(binary): """ 将二进制转换为IP地址 :param binary: :return: """ ip_str = "" binary_list = binary.split(" ") for num in binary_list: ip_str += str(int(num, 2)) + "." return ip_str[:-1] print(binary_to_decimal(binary_ip))
2.python递归的默认最大层数(1分),怎么更改其默认最大层数?(2分)(本题总分3分)
# import sys # sys.setrecursionlimit(100000) # # def func(n): # print(n) # n += 1 # func(n) # # func(0)
3.请写出print的结果:(3分)
v1 = 1 or 3
v2 = 1 and 3
v3 = 0 and 2 and 1
v4 = 0 and 2 or 1
v5 = 0 and 2 or 1 or 4
v6 = 0 or False and 1
print(v1, v2, v3, v4, v5, v6)
答:
1,3,0,1,1,False
4.用一行代码实现数值交换:(2分)
a = 1
b = 2
答:
a, b = b, a
5.如何安装第三方模块?(1分)以及用过哪些第三方模块?(2分)(本题总分3分)
方法1:pip install 第三方模块 方法2:pycharm中鼠标操作 用过:greenlet gevent pymysql socket
6.谈谈你对闭包的理解?(2分)并写一个闭包函数。(2分)(本题总分4分)
内层函数对外层函数非全局变量的引用,该内部函数称为闭包函数 def func(): name = ‘666‘ def inner(): print(name) return inner f = func() f()
7.什么是反射?(2分)以及应用场景?(2分)(本题总分4分)
通过字符串的形式操作对象相关的属性。python中的一切事物都是对象(都可以使用反射) 应用场景: class Foo: f = ‘类的静态变量‘ def __init__(self,name,age): self.name=name self.age=age def say_hi(self): print(‘hi,%s‘%self.name) obj=Foo(‘egon‘,73) #检测是否含有某属性 print(hasattr(obj,‘name‘)) print(hasattr(obj,‘say_hi‘)) #获取属性 n=getattr(obj,‘name‘) print(n) func=getattr(obj,‘say_hi‘) func() print(getattr(obj,‘aaaaaaaa‘,‘不存在啊‘)) #报错 #设置属性 setattr(obj,‘sb‘,True) setattr(obj,‘show_name‘,lambda self:self.name+‘sb‘) print(obj.__dict__) print(obj.show_name(obj)) #删除属性 delattr(obj,‘age‘) delattr(obj,‘show_name‘) delattr(obj,‘show_name111‘)#不存在,则报错 print(obj.__dict__)
8.什么是面向对象的mro?(3分)
答 :
对于你定义的每一个类,Python 会计算出一个方法解析顺序(Method Resolution Order, MRO)列表,它代表了新式类中类继承的顺序
9.简述 yield和yield from关键字。(3分)
答 :
在python中用yield和yield from都是构造一个生成器函数 yield后面返回的是一个值 yield from则是直接返回一个生成器
10.简述 OSI 七层协议。(写出7层2分,简述每一层总分3分)(本题总分5分)
答 :
应用层 网络服务与最终用户的一个接口。 常见协议:HTTP FTP TFTP SMTP SNMP DNS TELNET HTTPS POP3 DHCP 表示层 数据的表示、安全、压缩。(在五层模型里面已经合并到了应用层) 格式有,JPEG、ASCll、DECOIC、加密格式等 会话层 建立、管理、终止会话。(在五层模型里面已经合并到了应用层) 对应主机进程,指本地主机与远程主机正在进行的会话 传输层 定义传输数据的协议端口号,以及流控和差错校验。 常见协议:TCP UDP,数据包一旦离开网卡即进入网络传输层 常见的物理设备 :四层路由器、四层交换机 网络层 进行逻辑地址寻址,实现不同网络之间的路径选择。 常见协议:ICMP IGMP IP(IPV4 IPV6) ARP RARP 常见物理设备:路由器、三层交换机 数据链路层 建立逻辑连接、进行硬件地址寻址、差错校验 [2] 等功能。(由底层网络定义协议) 将比特组合成字节进而组合成帧,用MAC地址访问介质,错误发现但不能纠正。 ,常见协议:ARP协议 常见物理设备:网桥、以太网交换机、网卡 物理层 建立、维护、断开物理连接。(由底层网络定义协议) 常见物理设备:中继器、集线器、双绞线
11.什么是C/S和B/S架构?(2分)
答 :
Client与Server ,中文意思:客户端与服务器端架构,这种架构也是从用户层面(也可以是物理层面)来划分的。 这里的客户端一般泛指客户端应用程序EXE,程序需要先安装后,才能运行在用户的电脑上,对用户的电脑操作系统环境依赖较大。 B/S即:Browser与Server,中文意思:浏览器端与服务器端架构,这种架构是从用户层面来划分的。 Browser浏览器,其实也是一种Client客户端,只是这个客户端不需要大家去安装什么应用程序,只需在浏览器上通过HTTP请求服务器端相关的资源(网页资源),客户端Browser浏览器就能进行增删改查。
12.简述 三次握手、四次挥手的流程。(4分)
TCP的三次握手 TCP是因特网中的传输层协议,使用三次握手协议建立连接。当主动方发出SYN连接请求后,等待对方回答SYN+ACK[1],并最终对对方的 SYN 执行 ACK 确认。这种建立连接的方法可以防止产生错误的连接。[1] TCP三次握手的过程如下: 客户端发送SYN(SEQ=x)报文给服务器端,进入SYN_SEND状态。 服务器端收到SYN报文,回应一个SYN (SEQ=y)ACK(ACK=x+1)报文,进入SYN_RECV状态。 客户端收到服务器端的SYN报文,回应一个ACK(ACK=y+1)报文,进入Established状态。 三次握手完成,TCP客户端和服务器端成功地建立连接,可以开始传输数据了。 TCP的四次挥手 建立一个连接需要三次握手,而终止一个连接要经过四次握手,这是由TCP的半关闭(half-close)造成的。 (1) 某个应用进程首先调用close,称该端执行“主动关闭”(active close)。该端的TCP于是发送一个FIN分节,表示数据发送完毕。 (2) 接收到这个FIN的对端执行 “被动关闭”(passive close),这个FIN由TCP确认。 注意:FIN的接收也作为一个文件结束符(end-of-file)传递给接收端应用进程,放在已排队等候该应用进程接收的任何其他数据之后,因为,FIN的接收意味着接收端应用进程在相应连接上再无额外数据可接收。 (3) 一段时间后,接收到这个文件结束符的应用进程将调用close关闭它的套接字。这导致它的TCP也发送一个FIN。 (4) 接收这个最终FIN的原发送端TCP(即执行主动关闭的那一端)确认这个FIN。[1] 既然每个方向都需要一个FIN和一个ACK,因此通常需要4个分节。 注意: (1) “通常”是指,某些情况下,步骤1的FIN随数据一起发送,另外,步骤2和步骤3发送的分节都出自执行被动关闭那一端,有可能被合并成一个分节。[2] (2) 在步骤2与步骤3之间,从执行被动关闭一端到执行主动关闭一端流动数据是可能的,这称为“半关闭”(half-close)。 (3) 当一个Unix进程无论自愿地(调用exit或从main函数返回)还是非自愿地(收到一个终止本进程的信号)终止时,所有打开的描述符都被关闭,这也导致仍然打开的任何TCP连接上也发出一个FIN。 无论是客户还是服务器,任何一端都可以执行主动关闭。通常情况是,客户执行主动关闭,但是某些协议,例如,HTTP/1.0却由服务器执行主动关闭。[2]
13.什么是arp协议?(2分)
地址解析协议,即ARP(Address Resolution Protocol),是根据IP地址获取物理地址的一个TCP/IP协议。
14.TCP和UDP的区别?(3分)
TCP(Transmission Control Protocol)可靠的、面向连接的协议(eg:打电话)、传输效率低全双工通信(发送缓存&接收缓存)、面向字节流。使用TCP的应用:Web浏览器;电子邮件、文件传输程序。 UDP(User Datagram Protocol)不可靠的、无连接的服务,传输效率高(发送前时延小),一对一、一对多、多对一、多对多、面向报文,尽最大努力服务,无拥塞控制。使用UDP的应用:域名系统 (DNS);视频流;IP语音(VoIP)。
15.为何基于tcp协议的通信比基于udp协议的通信更可靠?(2分)
当客户和服务器彼此交换数据前,必须先在双方之间建立一个TCP连接,之后才能传输数据。TCP提供超时重发,丢弃重复数据,流量控制等功能,保证能从一端传到另一端。
16.什么是粘包?(1分) socket 中造成粘包的原因是什么?(2分) 哪些情况会发生粘包现象?(2分)(本题总分5分)
粘包 :在tcp协议中,几条连续发送的数据在接收端粘在一起。 粘包成因 :本质是发送信息与接受信息的边界不清晰造成的 哪些情况会发生粘包 :发送端的缓存机制、接收端的缓存机制
17.什么是进程?(2分)请简述进程的三个状态?(2分)(本题总分4分)
在运行中的程序就是进程 进程是计算机中最小的资源分配单位 进程的三状态 :就绪 运行 阻塞
18.什么是线程?(3分)
线程是计算机中能被CPU调度的最小单位
19.简述 进程、线程、协程的区别?(3分) 以及应用场景?(3分)(本题总分6分)
进程:数据隔离,可以利用多核,会产生数据不安全的现象。常用于数据隔离度高的高计算型场景 线程:数据共享,在python中不能利用多核,操作系统级别,会产生数据不安全的现象。常用于数据隔离度低的高IO型场景。 协程:数据共享,不能利用多核,用户级,操作系统不可见,不存在数据安全问题。常用于数据隔离度低的高IO型场景。
20.GIL是什么?(3分)
全局解释器锁。 该锁是Cpython解释器中特有的 保证了同一个python进程中的多个线程同一时刻只能有一条线程访问CPU。
21.Python中如何使用线程池和进程池?(4分)
线程池 from concurrent.futures import ThreadPoolExecutor p = ThreadPoolExecutor(5) 进程池 from concurrent.futures import ProcessPoolExecutor p = ProcessPoolExecutor(5)
22.进程之间如何进行通信?(3分)
可以通过原生socket通信 如果是在同一个进程中,也可以使用multiprocessing自带的pipe或者queue进行通信 也可以使用第三方工具:memcache、redis、kafka、rabbitmq
23.什么是并发和并行?(3分)
并行 : 并行是指两者同时执行,比如赛跑,两个人都在不停的往前跑;(资源够用,比如三个线程,四核的CPU ) 并发 : 并发是指资源有限的情况下,两者交替轮流使用资源,比如一段路(单核CPU资源)同时只能过一个人,A走一段后,让给B,B用完继续给A ,交替使用,目的是提高效率。
24.你知道几种锁(请列出)?(1分)并解释它们的作用和区别?(3分)(本题总分4分)
互斥锁和递归锁 都是为了保证在并发编程中的数据安全问题 互斥锁 :在一个进程或者线程单位中只能连续调用一次,否则就会发生死锁现象 递归锁 :在Python中为了支持在同一线程中多次请求同一资源,python提供了可重入锁RLock
25.解释什么是异步非阻塞?(4分)
异步:程序在做多件事情的时候,不必按照顺序执行,可以各自执行各自的不受干扰 非阻塞:程序中没有阻塞现象
26.简述 gevent模块的作用和应用场景?(4分)
协程模块 可以用来规避单线程中的IO操作,提高线程对CPU的利用率 可用于 爬虫等IO操作比较频繁的网络应用场景
27.请实现功能 : 在不改变装饰器的情况下,给装饰器的执行加上log信息。(5分)
def log(wrap):
…
@log
def wrapper(func):
def inner(*args,**kwargs):
print(‘before func‘)
ret = func(*args,**kwargs)
print(‘after func‘)
return ret
return inner
@wrapper
def wahaha(name):
print(‘%s in wahaha‘%name)
wahaha(‘alex‘)
答:
def log(wrap): def log_inner(*args,**kwargs): inn = wrap(*args,**kwargs) def inner(*args,**kwargs): ret = inn(*args,**kwargs) print(wrap.__name__,‘is running‘) return ret return inner return log_inner @log def wrapper(func): def inner(*args,**kwargs): print(‘before func‘) ret = func(*args,**kwargs) print(‘after func‘) return ret return inner @wrapper def wahaha(name): print(‘%s in wahaha‘%name) wahaha(‘alex‘)
28.写代码得到两个列表的交集和差集?(4分)
l1 = [1,2,3]
l2 = [2,3,4]
# 交集 print(set(l1) & set(l2)) print(set(l1).intersection(set(l2))) # 差集 print(set(l1) - set(l2)) print(set(l1).difference(set(l2)))
29.用python正则匹配字符串,<.*>和<.*?>有什么区别?(3分)
<.*>是正则默认的贪婪匹配模式,会直接匹配到最后一个> <.*?>是正则表达式的惰性匹配模式,会匹配到第一个> 例如 <h1>hahaha<\h1> <.*> 匹配的结果是一个 <h1>hahaha<\h1> <.*?> 匹配的结果是两个<h1>和<\h1>
30.读代码写出代码运行后的结果,并解释执行过程?(6分)
def func(num):
n, a, b = 0, 0, 1
while n < num:
yield b
print(b, ‘in func‘)
a, b = b, a + b
n = n + 1
for i in func(5):
print(i)
yield: 带有yield的函数是一个迭代器,函数返回某个值时,会停留在某个位置,返回函数值后,会在前面停留的位置继续执行,直到程序结束
结果是: 1 1 in func 1 1 in func 2 2 in func 3 3 in func 5 5 in func
31.用Python实现一个二分查找的函数。(6分)
32.读代码写答案:(4分)
(1)
a = range(10)
b = range(20)
z = zip(a, b)
print(len(list(z))) # 请写出打印结果
print(len(list(z))) # 请写出打印结果
答:
zip() 函数用于将可迭代的对象作为参数,将对象中对应的元素打包成一个个元组,然后返回由这些元组组成的列表。
执行结果:
10 0
(2)
def func(n):
return n % 2 == 1
newlist = filter(func, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
print(list(newlist)) # 请写出打印结果
答:
filter() 函数用于过滤序列,过滤掉不符合条件的元素,返回由符合条件元素组成的新列表。
执行结果:
[1, 3, 5, 7, 9]
(3)
a = map(lambda x, y: x + y, [1, 3, 5, 7, 9], [2, 4, 6, 8, 10])
print(list(a)) # 请写出打印结果
答:
map() 会根据提供的函数对指定序列做映射。
执行结果:
[3, 7, 11, 15, 19]
33.读代码写出打印的结果,并解释为什么是这样。(6分)
class A(object):
def func(self):
print(‘A‘)
class B(A):
def func(self):
super().func()
print(‘B‘)
class C(A):
def func(self):
super().func()
print(‘C‘)
class D(B,C):
def func(self):
super().func()
print(‘D‘)
D().func()
答案:
A C B D 这是新式类的继承顺序导致的,根据c3算法或者mro方法给出的结论都可以得出。
34.列举你所知道的面向对象的特殊方法,及其应用?(5分,每写一个得0.5分)
__len__ len 一个对象就会触发 __len__方法。 __hash__()方法应该返回一个32位长的整数,对与同一个对象,__hash__()方法应该总是返回相同的值 __str__ 如果一个类中定义了__str__方法,那么在打印 对象 时,默认输出该方法的返回值。 __repr__ 如果一个类中定义了__repr__方法,那么在repr(对象) 时,默认输出该方法的返回值。 __call__ 方法的执行是由对象后加括号触发的 __eq__ x==y 运算将会调用实例x的__eq__(self, other)方法 __del__ 析构方法,当对象在内存中被释放时,自动触发执行。 __new__ 构造方法__new__的执行是由创建对象触发的,即:对象 = 类名() item系列将对象视为字典使用时,就会触发item方法 __getitem__ __setitem__ __delitem__ __delattr__
35.用尽量多的方法实现单例模式。(8分)
class A: __instance = None def __init__(self,name,age): self.name =name self.age = age def __new__(cls, *args, **kwargs): if cls.__instance is None: obj = object.__new__(cls) cls.__instance = obj return cls.__instance
36.读代码回答其中的问题:(本题总分12分)
(1)
import threading
import time
def _wait():
time.sleep(60)
# FlagA
t = threading.Thread(target=_wait, daemon=False)
t.start()
# FlagB
# 问:程序从FlagA执行到FlagB的时间大致为多少秒?并简述为什么?(2分)
0秒左右,因为创建一个线程并启动它的时间非常短。
# 问:deamon=True时 程序从FlagA执行到FlagB的时间大致为多少秒?并简述为什么?(2分)
0秒左右,如果设置deamon=True那么相当于开启了一个守护线程,守护线程会等待主线程结束之后才结束,而主线程会等待所有非守护线程子线程结束之后才结束,在程序中没有其他的非守护线程的子线程,因此主线程的代码结束之后守护线程就会立即结束。
(2)
loop = int(1E7)
def _add(loop: int = 1):
global number
for _ in range(loop):
number += 1
def _sub(loop: int = 1):
global number
for _ in range(loop):
number -= 1
number = 0
ta = threading.Thread(target=_add, args=(loop,))
ts = threading.Thread(target=_sub, args=(loop,))
ta.start()
ts.start()
ta.join()
ts.join()
# 问此时的number是否一定为零?并简述为什么?(2分)
不一定,因为在CPU指令中,+=操作是被拆分为两条指令的,所以可能出现数据不安全的情况。
(3)
number = 0
ta = threading.Thread(target=_add, args=(loop,))
ts = threading.Thread(target=_sub, args=(loop,))
ta.start()
ta.join()
ts.start()
ts.join()
# 问此时的number是否一定为零?并简述为什么?(2分)
会,如果加上join就使得代码从异步变成同步了,一定不会发生数据不安全的问题。
(4)
loop = int(1E7)
def _add(loop: int = 1):
global numbers
for _ in range(loop):
numbers.append(0)
def _sub(loop: int = 1):
global numbers
for _ in range(loop):
while not numbers:
time.sleep(1E-8)
numbers.pop()
numbers = [0]
ta = threading.Thread(target=_add, args=(loop,))
ts = threading.Thread(target=_sub, args=(loop,))
ta.start()
ts.start()
ta.join()
ts.join()
# 问此时的numbers的长度是否一定为1?并简述为什么?(2分)
一定为1,因为列表的append操作是线程安全的。
(5)
numbers = [0]
ta = threading.Thread(target=_add, args=(loop,))
ts = threading.Thread(target=_sub, args=(loop,))
ta.start()
ta.join()
ts.start()
ts.join()
# 问此时的numbers的长度是否一定为1?并简述为什么?(2分)