1.偏函数

可以指定函数的参数值为固定的数字

解除了数量的限制

from functiontools import partial

func2 = partial(func , c = 1)

不指定 key,就是按位置传。

按关键字传,然后调用的时候也要按关键字传。

2.新协程

asyncawait关键字。

使用async修饰方法,

await后面切

进入第二个协程对象。

使用send调用。

还需要手动捕获异常

一般结合时间循环队列使用。

使用 asyncio 的 get_event_loop()

task = loop.create_task(g2)

添加回调函数

task.add_done_callback(func3)

loop.run_until_complete(task)

实现并发:

asyncio 的 map 方法

3.Socket编程

3.1UDP发送数据

例子

import socket

udp_socket = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)

data = "你好".encode()

add = ("192.168.1.1",8080)

udp_socket.sendto(data,add)

udp_socket.close()

3.2UDP接受数据

import socket

udp_socket = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)

data = "你好".encode()

add = ("10.25.208.79",8080)

udp_socket.sendto(data,add)

print("等待接受数据")

data = udp_socket.recvfrom(1024) # 缓存大小,即一次可以接受最大的数据量
print("响应:", data)

udp_socket.close()

recvfrom返回一个元组

  • 第一个是返回的数据
  • 第二个是对方的IP地址和端口

b表示二进制编码。需要decode()解码。

也可以指定绑定端口:

add = ("10.25.208.79", 8888)

udp_socket.bind(add)

但是 UDP 不关心接受方是否接受。只关心是否发送出去。

3.3TCP基础操作

主 socket 监听新来的连接,子 socket 来负责连接传输数据。

简单实现 TCP 客户端:

import socket

# socket.SOCK_STREAM是用来创建TCp的
tcp_client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

add = ("10.25.208.65", 8080)

tcp_client_socket.connect(add)

tcp_client_socket.send("喝喝".encode())

print("发送完成,等待接受")

while True:
    ret = tcp_client_socket.recv(1024)

    print(ret.decode("gbk"))

tcp_client_socket.close()

简单 TCP 服务器:

  1. 创建 socket
  2. 绑定 IP 和端口
  3. 开启监听
  4. 创建连接的 socket
  5. 进行服务
  6. 关闭新的 socket
  7. 关闭总 socket
import socket

# socket.SOCK_STREAM是用来创建TCp的
tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

add = ("10.25.208.65", 5555)
tcp_server_socket.bind(add)

# 指定最大连接
print("开启监听")
tcp_server_socket.listen(5)

# 会返回一个元组
# 第一个元素就是新socket,用来和客户端进行数据传输的
# 第二个元素是客户端的地址信息
client_socket, add = tcp_server_socket.accept()
print("有客户端连接")
print(client_socket)
print(add)

# 发送数据
client_socket.send("开始服务".encode())
retData = client_socket.recv(1024)
print("接受数据:",retData.decode("gbk"))

tcp_server_socket.close()

完善版本的 TCP 服务器:

import socket

# socket.SOCK_STREAM是用来创建TCp的
tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

add = ("10.25.208.65", 5555)
tcp_server_socket.bind(add)

# 指定最大连接
print("开启监听")
tcp_server_socket.listen(5)

# 会返回一个元组
# 第一个元素就是新socket,用来和客户端进行数据传输的
# 第二个元素是客户端的地址信息
try:
    while True:
        client_socket, add = tcp_server_socket.accept()
        print("有客户端连接")
        print(client_socket)
        print(add)

        # 发送数据
        client_socket.send("开始服务".encode())
        try:
            while True:
                retData = client_socket.recv(1024)
                if retData:
                    print("接受数据:", retData.decode("gbk"))
                else:
                    raise Exception("连接已关闭")
        except Exception as e:
            print("内部报错:", e)
        finally:
            client_socket.close()
except Exception as e:
    print(e)
finally:
    tcp_server_socket.close()

首次循环是为了服务多个客户端

第二次循环是为了单个客户端的多次发送消息

多进程版本

import multiprocessing
import socket


def client_server(client_socket, add):
    print("有客户端连接")
    print(client_socket)
    print(add)

    # 发送数据
    client_socket.send("开始服务".encode())

    try:
        while True:
            retData = client_socket.recv(1024)
            if retData:
                print("接受数据:", retData.decode("gbk"))
            else:
                raise Exception("连接已关闭")
    except Exception as e:
        print("内部报错:", e)
    finally:
        client_socket.close()


if __name__ == '__main__':
    # socket.SOCK_STREAM是用来创建TCp的
    tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

    add = ("10.25.208.65", 5555)
    tcp_server_socket.bind(add)

    # 指定最大连接
    print("开启监听")
    tcp_server_socket.listen(5)

    # 会返回一个元组
    # 第一个元素就是新socket,用来和客户端进行数据传输的
    # 第二个元素是客户端的地址信息
    try:
        while True:
            client_socket, add = tcp_server_socket.accept()

            p = multiprocessing.Process(target=client_server,args=(client_socket,add))

            p.start()

    except Exception as e:
        print(e)
    finally:
        tcp_server_socket.close()

多线程版本

import threading
import socket


def client_server(client_socket, add):
    print("有客户端连接")
    print(client_socket)
    print(add)

    # 发送数据
    client_socket.send("开始服务".encode())

    try:
        while True:
            retData = client_socket.recv(1024)
            if retData:
                print("接受数据:", retData.decode("gbk"))
            else:
                raise Exception("连接已关闭")
    except Exception as e:
        print(add, e)
    finally:
        client_socket.close()


if __name__ == '__main__':
    # socket.SOCK_STREAM是用来创建TCp的
    tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

    add = ("10.25.208.65", 5555)
    tcp_server_socket.bind(add)

    # 指定最大连接
    print("开启监听")
    tcp_server_socket.listen(5)

    # 会返回一个元组
    # 第一个元素就是新socket,用来和客户端进行数据传输的
    # 第二个元素是客户端的地址信息
    try:
        while True:
            client_socket, add = tcp_server_socket.accept()

            t = threading.Thread(target=client_server,args=(client_socket,add))

            t.start()

    except Exception as e:
        print(e)
    finally:
        tcp_server_socket.close()

协程版本

# import threading
import socket
import gevent
from gevent import monkey


def client_server(client_socket, add):
    print("有客户端连接")
    print(client_socket)
    print(add)

    # 发送数据
    client_socket.send("开始服务".encode())
    gevent.sleep(1)
    try:
        while True:
            retData = client_socket.recv(1024)
            if retData:
                print("接受数据:", retData.decode("gbk"))
            else:
                raise Exception("连接已关闭")
    except Exception as e:
        print(add, e)
    finally:
        client_socket.close()


if __name__ == '__main__':

    monkey.patch_all()

    # socket.SOCK_STREAM是用来创建TCP的
    tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

    add = ("10.25.208.65", 5555)
    tcp_server_socket.bind(add)

    # 指定最大连接
    print("开启监听")
    tcp_server_socket.listen(5)

    # 会返回一个元组
    # 第一个元素就是新socket,用来和客户端进行数据传输的
    # 第二个元素是客户端的地址信息
    try:
        while True:
            client_socket, add = tcp_server_socket.accept()

            gevent.spawn(client_server,client_socket, add)

    except Exception as e:
        print(e)
    finally:
        tcp_server_socket.close()

3.4TCP详解

3.4.1 三次握手

  1. 客户端:喂,你听得到么?

  2. 服务端:① 嗯,我听得到你说话。② 你听得到我说话么

  3. 客户端:嗯,我也听得到。

    开始聊天

  • 第一次握手:建立连接时,客户端发送 syn 包(syn=j)到服务器,并进入 SYN_SENT 状态,等待服务器确认;SYN:同步序列编号(Synchronize Sequence Numbers)。

  • 第二次握手:服务器收到 syn 包,必须确认客户的 SYN(ack=j+1),同时自己也发送一个 SYN 包(syn=k),即 SYN+ACK 包,此时服务器进入 SYN_RECV 状态;==其实可以理解为是两次发送数据,第一次确认,第二次自己也发送类似第一次握手的消息。但是,实际上只有一个数据包==

  • 第三次握手:客户端收到服务器的 SYN+ACK 包,向服务器发送确认包 ACK(ack=k+1),此包发送完毕,客户端和服务器进入 ESTABLISHED(TCP 连接成功)状态,完成三次握手。

3.4.2 四次挥手

  1. 客户端:我要关闭连接了,然后客户端进入半关闭状态。

  2. 服务端:好的,我知道了。

  3. 服务端:我也要关闭连接了,然后服务器进入半关闭状态。

  4. 客户端:好的,我知道了。

先由客户端向服务器端发送一个 FIN,请求关闭数据传输。

当服务器接收到客户端的 FIN 时,向客户端发送一个 ACK,其中 ack 的值等于 FIN+SEQ

然后服务器向客户端发送一个 FIN,告诉客户端应用程序关闭。

当客户端收到服务器端的 FIN 是,回复一个 ACK 给服务器端。其中 ack 的值等于 FIN+SEQ

为什么要 4 次挥手?

确保数据能够完整传输。

当被动方收到主动方的 FIN 报文通知时,它仅仅表示主动方没有数据再发送给被动方了。

但未必被动方所有的数据都完整的发送给了主动方,所以被动方不会马上关闭 SOCKET,它可能还需要发送一些数据给主动方后,

再发送 FIN 报文给主动方,告诉主动方同意关闭连接,所以这里的 ACK 报文和 FIN 报文多数情况下都是分开发送的。

4.元类

所有可以实例化对象的类,打印 type 都是元类

所有不可以实例化对象的东西,打印 type 都是父级类

num = int()
print(type(int))
print(num)
print(type(num))

out:
<class 'type'>
0
<class 'int'>

4.1 元类的使用

4.1.1 定义类:

type(类名,由父类名称组成的元组(针对继承的情况,可以为空),属性字典)

Dog = type("Dog",(),{})
dog = Dog()
print(type(Dog))
print(type(dog))

out:
<class 'type'>
<class '__main__.Dog'>

属性填入后,是在类属性中。

不是实例属性。

4.1.2 创建带方法的类

方法也是属性,类型是 Function 的。

直接在属性字典中添加就可以了。

def eat(self):
    print("小狗在吃")


Dog = type("Dog",(),{"eat":eat})

dog = Dog()

dog.eat()

out:
小狗在吃

注意,定义方法的时候,第一个参数必须为self

创建带实例属性的类,就是把init方法传

4.1.3metaclass属性

元类可以拦截类的创建过程,修改类创建的时候的一些内容。

例子:

创建类的时候将所有属性名都换成大写的。

def upper_attr_name(class_name,class_parents_name,class_attr):

    # 一些在创建类之前的操作
    print(class_attr)
    newAttr = {}
    for name, value in class_attr.items():
        if not name.startswith("__"):
            newAttr[name.upper()] = value

    return type(class_name,class_parents_name,newAttr)


class A(object,metaclass=upper_attr_name):
    name = "Hello A"


# class B(object):
#     name = "Hello B"
#     # python2 中这么写
#     __metaclass__ = upper_attr_name


# print(A.name)
print(A.NAME)


out:
{'__module__': '__main__', '__qualname__': 'A', 'name': 'Hello A'}
Hello A

5.垃圾回收

5.1 引用计数

对象被引用的时候引用计数+1,引用取消的时候计数-1,计数为 0 的时候会释放内存。

5.2 垃圾回收

内存超过一定值的时候会进行垃圾回收,保持良好的内存使用。

5.2.1 环形结构

环形结构永远不会被外部变量引用。

那么这两个变量就是垃圾

可以手动gc.collect()启动垃圾回收

5.2.2 分代回收

按某些规定扫描垃圾之后还存活的对象,会放入一代目。

下次扫描的时候不会扫描这些一代目的对象。

在某些次数过后,扫描一遍一代目。

还存活的放入二代目。

新建的都是零代目。