python面试笔试题,python面试题史上最全

  python面试笔试题,python面试题史上最全

  在解析实例是如工作之前,我们先看看socektserver类的继承关系图:

  请求类继承关系:

  计算机网络服务器类继承关系:

  有了上面的继承关系图后,我们解析实例就轻松多了,下面,我们从代码开始,慢慢揭开实例面纱:

  导入实例

  导入结构,json,os

  类FtpServer(socketserver .BaseRequestHandler):

  编码=utf-8

  服务器目录=文件上传

  最大数据包大小=1024

  BASE _ DIR=OS。路径。dirname(OS。路径。ABS路径(_ _ file _ _))

  定义句柄(自身):

  打印(自我请求)

  虽然正确:

  data=self.request.recv(4)

  data_len=struct.unpack(i ,data)[0]

  head _ JSON=self。请求。recv(数据长度).解码(自编码)

  head_dic=json.loads(head_json)

  cmd=head_dic[cmd]

  如果hasattr(self,cmd):

  func=getattr(self,cmd)

  func(head_dic)

  定义put(自身):

  及格

  定义获取(自身):

  及格

  if __name__==__main__ :

  主机,端口=本地主机,9999

  使用socketserver .ThreadingTCPServer((主机,端口),FtpServer)作为服务器:

  server.serve_forever()

  我们通过socketserver .ThreadingTCPServer实例化对象服务器,那么此时应用调用类的__init__方法,前往ThreadingTCPServer类看看:

  类threading cpserver(threading mixin,UDPServer): pass发现這个类啥都没写,我们知道,如果一个类什么方法都没有定义,那么它的方法肯定都是从其父类继承而来,接着,先到穿线最小值里面看看,

  类线程混合:

  daemon_threads=False

  定义进程请求线程(自身,请求,客户端地址):

  passdef进程请求(自身,请求,客户端地址):

  及格这个类也没有__init__方法,因此,我们应该去右继承的父类服务模块中找:

  服务模块类(基础服务器):

  地址_家庭=套接字AF_INET。

  socket_type=套接字。袜子_流

  请求队列大小=5

  允许重用地址=假

  def __init__(self,server_address,RequestHandlerClass,bind_and_activate=True):

  基础服务器.__init__(self,server_address,RequestHandlerClass)#

  自我。插座=插座。插座(自身。address _ family,self.socket_type) #创建套接字对象

  如果绑定并激活:

  尝试:

  self.server_bind() #绑定端口和互联网协议(互联网协议)

  self.server_activate() #监听端口

  除了:

  self.server_close()

  上升

  看到服务模块的__init__方法,完成了以下几件事:

  创建套接字,绑定端口和IP,并监听

  将端口、IP和我们创建类传递到基础服务器类中;

  此时,对象的初始化工作并没有完成,接着,我们要进入基础服务器类,看看该类下的__init__完成了什么工作:

  类库服务器:

  超时=无

  def __init__(self,server_address,RequestHandlerClass):

  self.server_address=服务器_地址#将端口和互联网协议(互联网协议)暂存

  自我RequestHandlerClass=RequestHandlerClass #暂存我们创建的类

  自我. is_shut_down=线程。事件()#创建事件对象到此,对象的初始化工作完成。然后是调用永远服务()方法,开始不断循环监听。下面,我们来看看,这个服务器_永远实现

  注意:我们要清楚一点,我们在找這个方法在哪里的时候,一定要按照顺序去找,也就是说,我们先得从子类开始找,如果子类不存在,就去其父类找。下面我们就遵循這个原则来找找看。

  先来看看左继承的父类穿线米辛中有没有永远的服务器:

  类线程混合:

  daemon_threads=False

  定义进程请求线程(自身,请求,客户端地址):

  尝试:

  self.finish_request请求,客户端地址)

  例外情况除外:

  self.handle_error(请求,客户端地址)

  最后:

  self.shutdown _请求(请求)

  定义流程请求(自身,请求,客户端地址):

  t=螺纹。线程(目标=自己。进程请求线程,

  args=(请求,客户端地址))

  t.daemon=self.daemon_threads

  启动()

  再来看看父类三氯苯酚服务器:

  class TCP server(BaseServer):def _ _ init _ _(self,server_address,RequestHandlerClass,bind_and_activate=True):

  定义服务器_绑定(自身):

  定义服务器_激活(自身):

  定义服务器_关闭(自身):

  定义文件号(自身):

  定义get_request(自身):

  定义关闭请求(自身,请求):

  定义关闭请求(自身,请求):

  我们发现,没有服务器_永远方法,好,我去其继承的父类基础服务器类看看:

  class base server:def _ _ init _ _(self,server_address,RequestHandlerClass):

  定义服务器_激活(自身):

  def serve_forever(self,poll_interval=0.5):

  定义关机(自我):

  定义服务_活动(自身):

  定义句柄_请求(自身):

  def _ handle _ request _ no block(自身):

  定义句柄_超时(自身):

  定义验证请求(自身,请求,客户端地址):

  定义流程请求(自身,请求,客户端地址):

  定义服务器_关闭(自身):

  定义完成请求(自身,请求,客户端地址):

  定义关闭请求(自身,请求):

  定义关闭请求(自身,请求):

  定义句柄_错误(自身,请求,客户端_地址):

  def __enter__(自身):

  def __exit__(self,*args):

  我们发现server_forever()果然在這个类中,现在,我们的目标是:找到在什么地方调用我们自己写的处理方法。

  在我们找到的server_forever()方法中,

  def serve_forever(self,poll_interval=0.5):

  自我_ _ is _ shut _ down.clear()

  尝试:

  使用_ServerSelector()作为选择器:

  选择器.寄存器(自身,选择器。事件_读取)#原来底层是用使用来实现不断循环监听

  而不是自我。_ _关闭请求:

  就绪=选择器。select(poll_interval) #有新的链接进来

  如果准备好了:

  自我. handle_request_noblock() #这里应该是处理新的链接

  self.service_actions()

  最后:

  自我。_ _关闭请求=假

  自我. is_shut_down.set()

  好,我大致找到了链接的处理入口,我们跟进去,继续寻找:

  def _ handle _ request _ no block(自身):

  尝试:

  请求,客户端地址=self.get请求()

  除了o错误:

  返回

  if self.verify_request(请求,客户端地址):

  尝试:

  self.process _请求(请求,客户端地址)#注意这里的流程请求()

  例外情况除外:

  self.handle_error(请求,客户端地址)

  self.shutdown _请求(请求)

  除了:

  self.shutdown _请求(请求)

  上升

  否则:

  self.shutdown _请求(请求)

  到源码中,我们找到该函数,现在,只看我划线的部分。其他部分都是针对异常的处理,如果没有异常,其他都是不会执行的,所以,其他的异常处理,我们先暂时不看。

  我们发现,如果有链接,最后会交给进程请求()(我们会发现,在基础服务器类和穿线米辛都有這个方法,这里找类方法,一定要按照类的继承顺序来查找),所以,我们到穿线米辛中去看看processs _ request()做了哪些事情:

  定义流程请求(自身,请求,客户端地址):

  t=螺纹。线程(目标=自己。process _ request _ thread,args=(请求,客户端地址))#原来开了一个线程,支持并发

  t.daemon=self.daemon_threads #开启守护线程

  启动()在线程中执行该类下的process_requsest_thread()方法,

  定义进程请求线程(自身,请求,客户端地址):

  尝试:

  self.finish_request请求,客户端地址)

  例外情况除外:

  self.handle_error(请求,客户端地址)

  最后:

  self.shutdown _请求(请求)

  到此为止,链接建立成功!

  下面,我们来看看,当有消息发送,是如何进行处理的。

  当有消息发送,选择器监听到了,

  def serve_forever(self,poll_interval=0.5):

  自我_ _ is _ shut _ down.clear()

  尝试:

  使用_ServerSelector()作为选择器:

  选择器.寄存器(自身,选择器。事件_读取)#监听了活动链接

  而不是自我。_ _关闭请求:

  就绪=选择器。选择(轮询间隔)

  如果准备好了:#准备好了

  自我. handle_request_noblock() #进入处理

  self.service_actions()

  最后:

  自我。_ _关闭请求=假

  自我. is_shut_down.set()

  下面我们跟进_handle_request_noblock(),

  def _ handle _ request _ no block(自身):

  尝试:

  请求,客户端地址=self.get请求()

  除了o错误:

  返回

  if self.verify_request(请求,客户端地址):

  尝试:

  self.process _请求(请求,客户端地址)

  例外情况除外:

  self.handle_error(请求,客户端地址)

  self.shutdown _请求(请求)

  除了:

  self.shutdown _请求(请求)

  上升

  否则:

  self.shutdown _请求(请求)

  我们到流程请求()看看:

  定义流程请求(自身,请求,客户端地址):

  启动一个新线程来处理请求。

  t=螺纹.线程(目标=自身。进程请求线程,#启动一个线程来处理请求

  args=(请求,客户端地址))

  t.daemon=self.daemon_threads

  启动()

  然后开启线程执行,进程请求线程()方法,

  定义进程请求线程(自身,请求,客户端地址):

  尝试:

  self.finish_request(request,client _ address)#-到基础服务器查找

  例外情况除外:

  self.handle_error(请求,客户端地址)

  最后:

  self.shutdown _请求(请求)

  然后调用完成请求()方法,现在我们跟进看看,

  定义完成请求(自身,请求,客户端地址):

  自我RequestHandlerClass(请求,客户端地址,自身)执行了请求句柄类(请求,客户端地址,自身),這个是啥?还记得最开始我们传进来的类保存在哪呢?没错,就是RequestHandlerClass里面,现在这里才开始实例化這个类,也就是说,在这里开始调用我们自己的类了。既然是调用我们自己的类,那么必然要实例化,我们先回到自己创建的类,找找__init__方法。

  类MyTCPHandler(socketserver .BaseRequestHandler):

  定义句柄(自身):

  自我。数据=自身。请求。第1024号建议.条状()

  打印( {}写道:。格式(self.client_address[0])

  打印(自我数据)

  自我。请求。sendall(自我。数据。upper())自己类没有写__init__方法,那么我去它继承的BaseRequestHandler()下面找找看:

  类BaseRequestHandler:

  def __init__(自身,请求,客户端地址,服务器):

  自我请求=请求编号接受传进来的请求链接

  self.client_address=客户端地址#客户端的ip/端口

  self.server=服务器#

  self.setup()

  尝试:

  self.handle()

  最后:

  self.finish()

  定义设置(自身):

  及格

  定义句柄(自身):

  及格

  定义完成时间(自身):

  及格

  我们来看看,它继承类实例化完成了哪些操作:

  调用手柄()方法,我们发现,在这个类中也有一个手柄()方法,那么这里调用时调用自己写的还是這个类中的呢?

  当然是调用我们自己写!

  至此,我们完成了一次通信的完整过程!

  总结sockerserver整个流程:

  1.开启了线程,支持并发操作

  2.输入-输出多路复用,监听多个文件描述符!

  参考

  在解析实例是如工作之前,我们先看看socektserver类的继承关系图:

  请求类继承关系:

  计算机网络服务器类继承关系:

  有了上面的继承关系图后,我们解析实例就轻松多了,下面,我们从代码开始,慢慢揭开实例面纱:

  导入实例

  导入结构,json,os

  类FtpServer(socketserver .BaseRequestHandler):

  编码=utf-8

  服务器目录=文件上传

  最大数据包大小=1024

  BASE _ DIR=OS。路径。dirname(OS。路径。ABS路径(_ _ file _ _))

  定义句柄(自身):

  打印(自我请求)

  虽然正确:

  data=self.request.recv(4)

  data_len=struct.unpack(i ,data)[0]

  head _ JSON=self。请求。recv(数据长度).解码(自编码)

  head_dic=json.loads(head_json)

  cmd=head_dic[cmd]

  如果hasattr(self,cmd):

  func=getattr(self,cmd)

  func(head_dic)

  定义put(自身):

  及格

  定义获取(自身):

  及格

  if __name__==__main__ :

  主机,端口=本地主机,9999

  使用socketserver .ThreadingTCPServer((主机,端口),FtpServer)作为服务器:

  server.serve_forever()

  我们通过socketserver .ThreadingTCPServer实例化对象服务器,那么此时应用调用类的__init__方法,前往ThreadingTCPServer类看看:

  类threading cpserver(threading mixin,UDPServer): pass发现這个类啥都没写,我们知道,如果一个类什么方法都没有定义,那么它的方法肯定都是从其父类继承而来,接着,先到穿线最小值里面看看,

  类线程混合:

  daemon_threads=False

  定义进程请求线程(自身,请求,客户端地址):

  passdef进程请求(自身,请求,客户端地址):

  及格这个类也没有__init__方法,因此,我们应该去右继承的父类服务模块中找:

  服务模块类(基础服务器):

  地址_家庭=套接字AF_INET。

  socket_type=套接字。袜子_流

  请求队列大小=5

  允许重用地址=假

  def __init__(self,server_address,RequestHandlerClass,bind_and_activate=True):

  基础服务器.__init__(self,server_address,RequestHandlerClass)#

  自我。插座=插座。插座(自身。address _ family,self.socket_type) #创建套接字对象

  如果绑定并激活:

  尝试:

  self.server_bind() #绑定端口和互联网协议(互联网协议)

  self.server_activate() #监听端口

  除了:

  self.server_close()

  上升

  看到服务模块的__init__方法,完成了以下几件事:

  创建套接字,绑定端口和IP,并监听

  将端口、IP和我们创建类传递到基础服务器类中;

  此时,对象的初始化工作并没有完成,接着,我们要进入基础服务器类,看看该类下的__init__完成了什么工作:

  类库服务器:

  超时=无

  def __init__(self,server_address,RequestHandlerClass):

  self.server_address=服务器_地址#将端口和互联网协议(互联网协议)暂存

  自我RequestHandlerClass=RequestHandlerClass #暂存我们创建的类

  自我. is_shut_down=线程。事件()#创建事件对象到此,对象的初始化工作完成。然后是调用永远服务()方法,开始不断循环监听。下面,我们来看看,这个服务器_永远实现

  注意:我们要清楚一点,我们在找這个方法在哪里的时候,一定要按照顺序去找,也就是说,我们先得从子类开始找,如果子类不存在,就去其父类找。下面我们就遵循這个原则来找找看。

  先来看看左继承的父类穿线米辛中有没有永远的服务器:

  类线程混合:

  daemon_threads=False

  定义进程请求线程(自身,请求,客户端地址):

  尝试:

  self.finish_request请求,客户端地址)

  例外情况除外:

  self.handle_error(请求,客户端地址)

  最后:

  self.shutdown _请求(请求)

  定义流程请求(自身,请求,客户端地址):

  t=螺纹。线程(目标=自己。进程请求线程,

  args=(请求,客户端地址))

  t.daemon=self.daemon_threads

  启动()

  再来看看父类三氯苯酚服务器:

  class TCP server(BaseServer):def _ _ init _ _(self,server_address,RequestHandlerClass,bind_and_activate=True):

  定义服务器_绑定(自身):

  定义服务器_激活(自身):

  定义服务器_关闭(自身):

  定义文件号(自身):

  定义get_request(自身):

  定义关闭请求(自身,请求):

  定义关闭请求(自身,请求):

  我们发现,没有服务器_永远方法,好,我去其继承的父类基础服务器类看看:

  class base server:def _ _ init _ _(self,server_address,RequestHandlerClass):

  定义服务器_激活(自身):

  def serve_forever(self,poll_interval=0.5):

  定义关机(自我):

  定义服务_活动(自身):

  定义句柄_请求(自身):

  def _ handle _ request _ no block(自身):

  定义句柄_超时(自身):

  定义验证请求(自身,请求,客户端地址):

  定义流程请求(自身,请求,客户端地址):

  定义服务器_关闭(自身):

  定义完成请求(自身,请求,客户端地址):

  定义关闭请求(自身,请求):

  定义关闭请求(自身,请求):

  定义句柄_错误(自身,请求,客户端_地址):

  def __enter__(自身):

  def __exit__(self,*args):

  我们发现server_forever()果然在這个类中,现在,我们的目标是:找到在什么地方调用我们自己写的处理方法。

  在我们找到的server_forever()方法中,

  def serve_forever(self,poll_interval=0.5):

  自我_ _ is _ shut _ down.clear()

  尝试:

  使用_ServerSelector()作为选择器:

  选择器.寄存器(自身,选择器。事件_读取)#原来底层是用使用来实现不断循环监听

  而不是自我。_ _关闭请求:

  就绪=选择器。select(poll_interval) #有新的链接进来

  如果准备好了:

  自我. handle_request_noblock() #这里应该是处理新的链接

  self.service_actions()

  最后:

  自我。_ _关闭请求=假

  自我. is_shut_down.set()

  好,我大致找到了链接的处理入口,我们跟进去,继续寻找:

  def _ handle _ request _ no block(自身):

  尝试:

  请求,客户端地址=self.get请求()

  除了o错误:

  返回

  if self.verify_request(请求,客户端地址):

  尝试:

  self.process _请求(请求,客户端地址)#注意这里的流程请求()

  例外情况除外:

  self.handle_error(请求,客户端地址)

  self.shutdown _请求(请求)

  除了:

  self.shutdown _请求(请求)

  上升

  否则:

  self.shutdown _请求(请求)

  到源码中,我们找到该函数,现在,只看我划线的部分。其他部分都是针对异常的处理,如果没有异常,其他都是不会执行的,所以,其他的异常处理,我们先暂时不看。

  我们发现,如果有链接,最后会交给进程请求()(我们会发现,在基础服务器类和穿线米辛都有這个方法,这里找类方法,一定要按照类的继承顺序来查找),所以,我们到穿线米辛中去看看processs _ request()做了哪些事情:

  定义流程请求(自身,请求,客户端地址):

  t=螺纹。线程(目标=自己。process _ request _ thread,args=(请求,客户端地址))#原来开了一个线程,支持并发

  t.daemon=self.daemon_threads #开启守护线程

  启动()在线程中执行该类下的process_requsest_thread()方法,

  定义进程请求线程(自身,请求,客户端地址):

  尝试:

  self.finish_request请求,客户端地址)

  例外情况除外:

  self.handle_error(请求,客户端地址)

  最后:

  self.shutdown _请求(请求)

  到此为止,链接建立成功!

  下面,我们来看看,当有消息发送,是如何进行处理的。

  当有消息发送,选择器监听到了,

  def serve_forever(self,poll_interval=0.5):

  自我_ _ is _ shut _ down.clear()

  尝试:

  使用_ServerSelector()作为选择器:

  选择器.寄存器(自身,选择器。事件_读取)#监听了活动链接

  而不是自我。_ _关闭请求:

  就绪=选择器。选择(轮询间隔)

  如果准备好了:#准备好了

  自我. handle_request_noblock() #进入处理

  self.service_actions()

  最后:

  自我。_ _关闭请求=假

  自我. is_shut_down.set()

  下面我们跟进_handle_request_noblock(),

  def _ handle _ request _ no block(自身):

  尝试:

  请求,客户端地址=self.get请求()

  除了o错误:

  返回

  if self.verify_request(请求,客户端地址):

  尝试:

  self.process _请求(请求,客户端地址)

  例外情况除外:

  self.handle_error(请求,客户端地址)

  self.shutdown _请求(请求)

  除了:

  self.shutdown _请求(请求)

  上升

  否则:

  self.shutdown _请求(请求)

  我们到流程请求()看看:

  定义流程请求(自身,请求,客户端地址):

  启动一个新线程来处理请求。

  t=螺纹.线程(目标=自身。进程请求线程,#启动一个线程来处理请求

  args=(请求,客户端地址))

  t.daemon=self.daemon_threads

  启动()

  然后开启线程执行,进程请求线程()方法,

  定义进程请求线程(自身,请求,客户端地址):

  尝试:

  self.finish_request(request,client _ address)#-到基础服务器查找

  例外情况除外:

  self.handle_error(请求,客户端地址)

  最后:

  self.shutdown _请求(请求)

  然后调用完成请求()方法,现在我们跟进看看,

  定义完成请求(自身,请求,客户端地址):

  自我RequestHandlerClass(请求,客户端地址,自身)执行了请求句柄类(请求,客户端地址,自身),這个是啥?还记得最开始我们传进来的类保存在哪呢?没错,就是RequestHandlerClass里面,现在这里才开始实例化這个类,也就是说,在这里开始调用我们自己的类了。既然是调用我们自己的类,那么必然要实例化,我们先回到自己创建的类,找找__init__方法。

  类MyTCPHandler(socketserver .BaseRequestHandler):

  定义句柄(自身):

  自我。数据=自身。请求。第1024号建议.条状()

  print({}写道:。format(self.client_address[0])

  打印(自我数据)

  自我。request.sendall (self。data.upper())没有在自己的类中编写__init__方法,所以我将在其继承的BaseRequestHandler()下查找它:

  类BaseRequestHandler:

  def __init__(自身,请求,客户端地址,服务器):

  Self.request=request #接受传入请求链接

  self . client _ address=client _ address #客户端的ip/端口

  self.server=服务器#

  self.setup()

  尝试:

  self.handle()

  最后:

  self.finish()

  定义设置(自身):

  及格

  定义句柄(自身):

  及格

  定义完成时间(自身):

  及格

  让我们看看它通过继承类实例化做了什么:

  在调用handle()方法时,我们发现这个类中也有一个handle()方法。那么,在这里调用的时候,是自己写的,还是这个班写的?

  当然,叫我们自己写!

  至此,我们完成了一个完整的沟通过程!

  总结sockerserver的整个流程:

  1.启动线程并支持并发操作。

  2.I/O多路复用,监听多个文件描述符!

  涉及

  转载请联系作者取得转载授权,否则将追究法律责任。

郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。

留言与评论(共有 条评论)
   
验证码: