zoukankan      html  css  js  c++  java
  • python周报第十周

    0.本周知识点预览

    • python作用域
    • 浅谈py2和py3的多继承
    • socketserver源码简析
    • IO多路复用
    • 初识多线程

    1.python作用域

    先看一个简单的例子

    例子1:

    def func():
        name = "lk"
    
    func()
    print(name)

    执行结果如下:

    Traceback (most recent call last):
      File "/Users/liukai/PycharmProjects/s13/day10/test.py", line 12, in <module>
        print(name)
    NameError: name 'name' is not defined

    例子2:

    if 1==1:
        name = "lk"
    
    print(name)

    执行结果如下:

    lk

    例子3:

    name = 'lk'
    
    def f1():
        print(name)
    
    def f2():
        name = "liukai"
        return f1
    
    ret = f2()
    ret()

    执行结果如下:

    lk

    例子4:

    li = [lambda :x for x in range(10)]
    print(li[0],li[1])
    print(li[0]())

    执行结果如下:

    <function <listcomp>.<lambda> at 0x1079470d0> <function <listcomp>.<lambda> at 0x107947158>
    9

     代码解析:

    例子1:变量name定义在函数func()内,当print(name)的时候,报变量未定义,可推断出,python至少作用域为函数级。

    例子2:在if else代码块内,定义了变量name,可以成功print,可见,python也并不是代码块级作用域。PS:java、c#是代码块级作用域。

    例子3:python为函数级作用域,而且内部函数未找到变量时,会去外层函数找,但例子3中,f1函数在f2函数内,不过输出的并不是我们想要的liukai,说明,python的作用域在代码执行之前就确定了,和函数如何嵌套是没关系的,代码从上到下执行,到加载到f1函数时,作用域就为函数体内,如若找不到变量,便去函数体外找,并不是在f2内。

    例子4:li列表的最终结果为10个lambda函数,如li[0],li[1]...,不过为什么li[0]不等于0呢?这是因为,for x in range(10),已经让x等于9,并且,函数如若不加括号,便不会执行,也不会加载函数体内的内容。所有函数体内的x值一直不变,当执行li[0]()的时候,便会输出此时已经等于9的x。

    综上:python的作用域为函数级,包括class,def,lambda;if else / while / for 不会改变作用域,搜索顺序为先搜索本地局部变量,在搜索上层变量直至全局变量。

    2.py2和py3的多继承

    1.py2

    这里先说python2.7的版本。python2.7版本的类分为两种。

    一、经典类

    class A:
        def fuck(self):
            print("我是A类")
    
    class B(A):
        def haha(self):
            print("我是B类")
    
    class C(A):
        def haha(self):
            print("我是C类")
    
    class D(B):
        def haha(self):
            print("我是D类")
    
    class E(C):
        def fuck(self):
            print("我是E类")
    
    class F(D,E):
        def haha(self):
            print("我是F类")
    
    f = F()
    f.fuck()

    执行结果如下:

    我是A类

    代码解析:python2.7的经典类的继承关系为深度优先,一直找到最顶层的父类,也就是一条道走到黑的模式。

    如图:

    二、新式类

    class A(object):
        def fuck(self):
            print("我是A类")
    
    class B(A):
        def haha(self):
            print("我是B类")
    
    class C(A):
        def haha(self):
            print("我是C类")
    
    class D(B):
        def haha(self):
            print("我是D类")
    
    class E(C):
        def fuck(self):
            print("我是E类")
    
    class F(D,E):
        def haha(self):
            print("我是F类")
    
    f = F()
    f.fuck()

    执行结果如下:

    我是E类

    代码解析:经典类和新式类在写法上的区别为 class A 和 class A (object),新式类的继承方式为广度优先。

    如图:

    2.py3

    python3的类继承举例:

    class A:
        def fuck(self):
            print("我是A类")
    
    class B(A):
        def haha(self):
            print("我是B类")
    
    class C(A):
        def haha(self):
            print("我是C类")
    
    class D(B):
        def haha(self):
            print("我是D类")
    
    class E(C):
        def fuck(self):
            print("我是E类")
    
    class F(D,E):
        def haha(self):
            print("我是F类")
    
    f = F()
    f.fuck()

    执行结果如下:

    我是E类

    代码解析:python3的类继承和python2.7的新式类的继承方式一样,都是广度优先。

    3.socketserver 源码简析

    socketserver源码举例:

    import socketserver         ###导入socketserver模块
    
    
    class MySocketServer(socketserver.BaseRequestHandler):      ###创建一个类继承socketserver.BaseRequestHandler
        def handle(self):
            pass
    
    if __name__ == '__main__':
        server = socketserver.ThreadingTCPServer(("127.0.0.1",8007),MySocketServer)     ###实例化socketserver.ThreadingTCPServer的对象,参数一是地址和端口,参数二是创建的类
        server.serve_forever()

    以下为socketserver源码的执行顺序

    ###找sockerserver 源码  -> socketserver.ThreadingTCPServer类
    ###1. TCPServer __init__
    
    ###2. BaseServer __init__    封装变量  self.server_address = server_address = ("127.0.0.1",8007)
    ###                                   self.RequestHandlerClass = RequestHandlerClass = MySocketServer
    
    ###3. server.serve_forever()
    
    ###4. 找BaseServer的serve_forever()  serve_forever()执行self._handle_request_noblock
    
    ###5. self._handle_request_noblock  执行  self.process_request()
    
    ###6. process_request 去 ThreadingMixIn 类里找,并执行了self.process_request_thread
    
    ###7. self.process_request_thread 执行了 self.finish_request , finish_request 去BaseServer 找
    ###8. finish_request 里执行了self.RequestHandlerClass(request, client_address, self)
    
    ###9. self.RequestHandlerClass(request, client_address, self) -> MySocketServer(request, client_address, self)
    
    ###10.  MySocketServer(request, client_address, self) 会执行 MySocketServer 的__init__方法
    
    ###11. MySocketServer 中没有 __init__方法,便去父类找,咱们写的父类是socketserver.BaseRequestHandler
    
    ###12. 父类中执行self.handle(),便是执行MySocketServer的handle()

    4.I/O多路复用 

     I/O多路复用,有三种方法,select、poll、epoll。

    windows适用于select

    mac 适用于select

    linux 适用于select、poll、epoll

    在这里只举例select的方法

    服务端:

    #!/usr/bin/env python3
    # -*- coding=utf-8 -*-
    # Author : LiuKai
    
    ## I/O 多路复用
    import socket       ###导入socket模块
    import select       ###导入select模块
    
    
    s = socket.socket()     ###创建一个socket
    
    s.bind(("127.0.0.1",9999,))     ###绑定地址
    
    
    s.listen(5)         ###设置等待队列
    input = [s,]        ###设置监听socket队列
    output = []         ###设置将要发送数据的socket队列
    
    while True:
        ###rlist 是当参数1 -> input中的socket发生变化时,就会将变化的socket加入到rlist中.变化是指当创建的socket接收请求(accept方法),或者创建连接的(conn方法)
        ###wlist 是只要参数2有值,就将socket对象传入wlist
        ###elist 是只要参数3的socket出现问题,就会把其加入到elist
        ###第四个参数1,代表超时时间,如若未设置,则select 会一直阻塞,直到文件句柄发生变化,如若设置为1,那么监听的socket无变化则阻塞1秒,返回空列表.
        rlist, wlist, elist = select.select(input,output,[],1)
    
        print(len(input),len(rlist),len(output),len(wlist))
        
        ###只要有新连接或者接收消息,则rlist就会添加元素
        for i in rlist:
            ###循环rlist,假如是新连接,则接受请求,把conn添加到input中,方便select 监听
            if i == s:
                conn, address = i.accept()
                input.append(conn)
                conn.sendall(bytes("hello",encoding="utf-8"))
            ###假如不是新连接,而是接收的信息(conn变化了),则如若接收没问题,则把连接(conn)假如到output列表中,方便wlist监听,发送消息.
            else:
                try:
                    data = i.recv(1024)
                    if not data:
                        raise Exception("收到空值")
                    else:
                        output.append(i)
                except:
                    ###假如客户端断开连接造成异常,则在监听队列中删除该conn
                    input.remove(i)
    
        ###从监听的即将要发送的等待队列中(conn),遍历该列表,发送消息,消息发出后,从发送队列中删除该conn.
        for j in wlist:
            j.sendall(bytes("response",encoding="utf-8"))
            output.remove(j)

    客户端:

    #!/usr/bin/env python3
    # -*- coding=utf-8 -*-
    # Author : LiuKai
    
    import socket
    
    s = socket.socket()
    
    s.connect(("127.0.0.1",9999,))
    
    data = s.recv(1024)
    print(data)
    
    while True:
        inp = input(">>>")
        s.sendall(bytes(inp,encoding="utf-8"))
        data = s.recv(1024)
        print(data)
    s.close()

    执行服务端后,执行多个客户端,客户端结果如下:

    b'hello'
    >>>ll
    b'response'
    >>>ll
    b'response'

    服务端结果如下:

    0 0 1 1
    1 1 0 0
    1 0 1 1
    1 1 0 0
    1 0 1 1
    1 0 0 0

    代码解析:服务端的结果为当创建一个连接,第一列和第二列为1,当新建的连接(第二列)发生变化后,连接用掉之后,会归0,当接收消息后,根据代码逻辑,会放入第三列一个socket待监听,致使第四列也会变为1.服务端发出消息后,第三列、第四列都归0.

    5.初识多线程

    简介:

    0.线程是应用程序中工作的最小单元。

    1.一个应用程序,可以有单、多进程,可以有单、多线程。

    2.默认:单进程、单线程

    3.当程序不怎么占用CPU时,I/O操作较频繁时,推荐用单进程、多线程。

    4.当程序多进行计算性操作,利用CPU较频繁,I/O操作较少时,推荐用单线程、多进程。

    5.由于线程之间是进行随机调度,并且每个线程可能只执行n条执行之后,当多个线程同时修改同一条数据时可能会出现脏数据,所以,出现了线程锁 - 同一时刻允许一个线程执行操作。

    不用多线程的代码:

    import time
    
    def f1(arg):
        time.sleep(1)
        print(arg)
    
    print(time.ctime())
    for i in range(5):
        f1(i)
    print(time.ctime())

    执行结果如下:

    Wed Jul 13 22:19:55 2016
    0
    1
    2
    3
    4
    Wed Jul 13 22:20:00 2016

    代码解析:可见,连续执行5次函数f1,执行的时间为5秒。

    以下为利用多线程的代码:

    import threading
    import time
    
    def f1(arg):
        time.sleep(1)
        print(arg)
    
    print(time.ctime())
    for i in range(5):
        t = threading.Thread(target=f1,args=(i,))
        t.start()
    print(time.ctime())

    执行结果如下:

    Wed Jul 13 22:25:38 2016
    Wed Jul 13 22:25:38 2016
    0
    2
    3
    4
    1

    代码解析:利用多线程,执行时间变为1秒。

  • 相关阅读:
    Apache JMeter 入门教程
    nacos 配置中心 & 服务发现 使用
    Apache Flink能用来做什么?
    使用etcd watch做服务发现
    服务的注册与发现(Consul、zookeeper、etcd、eureka、Nacos)
    四层、七层负载均衡的区别
    Apollo核心概念之“Namespace”
    Charles的原理、作用
    GIN框架中间件 C.NEXT() C.ABORT() C.SET() C.GET 跨中间件取值 GIN中间件中使用GOROUTINE
    gin内置验证器使用
  • 原文地址:https://www.cnblogs.com/Caesary/p/5662661.html
Copyright © 2011-2022 走看看