1.偏函数
可以指定函数的参数值为固定的数字
解除了数量的限制
from functiontools import partial
func2 = partial(func , c = 1)
不指定 key,就是按位置传。
按关键字传,然后调用的时候也要按关键字传。
2.新协程
async和await关键字。
使用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 服务器:
- 创建 socket
- 绑定 IP 和端口
- 开启监听
- 创建连接的 socket
- 进行服务
- 关闭新的 socket
- 关闭总 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 三次握手
-
客户端:喂,你听得到么?
-
服务端:① 嗯,我听得到你说话。② 你听得到我说话么
-
客户端:嗯,我也听得到。
开始聊天
-
第一次握手:建立连接时,客户端发送 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 四次挥手
-
客户端:我要关闭连接了,然后客户端进入半关闭状态。
-
服务端:好的,我知道了。
-
服务端:我也要关闭连接了,然后服务器进入半关闭状态。
-
客户端:好的,我知道了。
先由客户端向服务器端发送一个 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 分代回收
按某些规定扫描垃圾之后还存活的对象,会放入一代目。
下次扫描的时候不会扫描这些一代目的对象。
在某些次数过后,扫描一遍一代目。
还存活的放入二代目。
新建的都是零代目。