简介
- 上一篇介绍了python的迭代器和协程等知识点
- 这里重点说一下GIL、方法解析、类属性等问题
- Python最近又版本更新了,可以关注一下出了哪些新特性
- 任何一门语言都是一个完整的体系,但因为不同的特性,擅长处理的问题不一样
- 之前说过语言分两大块,编译型和解释型;如果能深入研究,从底层硬件开始了解,就又是一番风景
GIL锁
深浅拷贝
-
赋值操作有三类:直接赋值(值,无子对象);深拷贝、浅拷贝(都包含子对象) -
直接赋值:即等号 = ,变量相当于一个指针,指向某一对象(类似浅拷贝) a = 1
b = a
print(id(a))
print(id(b))
-
浅拷贝:如果对象内部不是简单的数值,而是列表之类的对象
- 浅拷贝只会拷贝父对象,不会拷贝对象内部的子对象;即子对象还是原对象的引用
>>> a = {1: [1,2,3], 2:'b'}
>>> b = a.copy()
>>> a, b
({1: [1, 2, 3], 2: 'b'}, {1: [1, 2, 3], 2: 'b'})
>>> a[1].append(4)
>>> a, b
({1: [1, 2, 3, 4], 2: 'b'}, {1: [1, 2, 3, 4], 2: 'b'})
>>> a[2] = 'c'
({1: [1, 2, 3, 4], 2: 'c'}, {1: [1, 2, 3, 4], 2: 'b'})
-
深拷贝:copy 模块的 deepcopy 方法,完全拷贝了父对象及其子对象;即完全独立 >>>import copy
>>> a = {1: [1,2,3,4]}
>>> c = copy.deepcopy(a)
>>> a, c
({1: [1, 2, 3, 4]}, {1: [1, 2, 3, 4]})
>>> a[1].append(5)
>>> a, c
({1: [1, 2, 3, 4, 5]}, {1: [1, 2, 3, 4]})
-
总结:可以将拷贝理解为新开辟独立空间,其他都是引用;深浅拷贝都拷贝父对象,但一个指向子对象,一个拷贝子对象
- Python中很多数据结构的操作都会copy出新对象,而不会在原对象上修改,例如切片
str = 'RoyKun'
result = str[0:5:1]
print(result)
import copy
a = [11,22]
b = [33,44]
c = [a,b]
d = c
e = copy.deepcopy(c)
c.append([55,66])
-
小结
- 字典:有自己的
copy() 方法(浅拷贝) - 函数传参一般都是引用,会修改原对象;如果不想,深拷贝!
- 应用场景:对数据测试一般先深拷贝,在此备份上操作
私有化
-
Python中没有C++中的public/protected/private等关键字控制作用域和继承方式,所以有了私有化的方式 xx: 公有变量,类似public
_x: 单前置下划线,私有化属性或方法,from somemodule import *禁止导入,类对象和子类可以访问,类似protected
__xx:双前置下划线,避免与子类中的属性命名冲突,无法在外部直接访问(名字重整所以访问不到),类似private
__xx__:双前后下划线,用户名字空间的魔法对象或属性。例如:__init__ , __ 不要自己发明这样的名字,特殊的public
xx_:单后置下划线,用于避免与Python关键词的冲突,常规自定义方法
-
总结:前置下划线意义特殊
网络编程
- 主要涉及TCP/UDP/HTTP等协议,使用
socket 包建立通信的方法
Linux基础
- Linux基本操作
ctrl A 到命令行首ctrl E 到命令行末ifconfig 查看网络状态mv 文件重命名cp 拷贝文件到 - vim基本操作
- 命令模式下:
- 直接跳转到某行:行号+G
- 复制光标所在行粘贴到下一行:yyp
- 跳到行末并进入编辑模式:A
- 跳到行首:I
- 选中后剪切:d
- 粘贴:p
- 选中后左移:<
- 在光标行前面插入一行:O
- 后插入一行:o
vim xxx.py +4 打开文件后光标在第四行 - 计算机网络基础知识
socket通信
-
完成网络通信必备的东西 -
import socket
socket.socket(AddressFamily, Type)
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.close()
-
在Linux中使用sublime编程,相关Linux操作可以看总结“Linux面试常考点” -
实现socket编程(见课件),缺那个Windows测试工具,应该类似postman吧 -
基于UDP协议发送数据:
from socket import *
udp_socket = socket(AF_INET, SOCK_DGRAM)
while True:
if send_data == "exit":
break
dest_addr = ('192.168.1.103', 7788)
send_data = input("请输入要发送的数据:")
udp_socket.sendto(send_data.encode('utf-8'), dest_addr)
udp_socket.close()
roy@ubuntu:$ python3 xxx.py
-
在发送之前ping 一下,看网络通不通;虚拟机要改成桥接模式,如果此时IP还是不在同一网段,使用命令sudo dhclient ,静静等待; -
错误提示TypeError: need is object not str意思就是别发字符串;解决方案:在字符串前面写个b ,即变成字节对象;或者如代码所示,encode -
在ubuntu中python3 和ipython3 都是交互模式,后者类似jupyter; -
UDP接收数据:
from socket import *
def main():
udp_socket = socket(AF_INET, SOCK_DGRAM)
local_addr = ('', 7788)
udp_socket.bind(local_addr)
recv_data = udp_socket.recvfrom(1024)
send_addr = recv_data[0]
recv_msg = recv_data[1]
print("%s:%s"%(str(send_addr), recv_msg.decode("gbk")))
udp_socket.close()
if __name__ == "__main__":
main()
-
小结:
- UDP协议的特点是不需要建立连接,只需要知道对方的IP和端口即可,数据的正确性依靠服务端校验
- 发送方和接收方各自搞清楚必要参数,发送方是对方的(IP,端口),接收方是自身(IP,端口);套接住咯,诶~
- 为何说“必要”参数呢?因为无论是作为发送方还是接收方,都需要有自己的端口才能发送数据的(tcp和udp是端对端的),但在这里发送方没有bind,所以OS会随机分配一个端口;
- 因为端口是套接字的对接目标,所以在同一电脑(IP)上的不同程序(端口)可以互发数据,“互发”意思就是创建一个套接字即可,可收可发;(将发送接收程序写在一起)
- 套接字是全双工的;
-
聊天器
- 程序在接收数据时若没有缓存消息,一闪而过,即OS会暂存收到的消息;这也存在弊端,可能会缓存过多信息,占用内存导致死机;(由于现在是单任务,只能实现半双工)
-
这里发送IP可以写127.0.0.1回环地址,自己发自己收;也可以查看ubuntu自身IP,实现自娱自乐;
TCP通信
-
UDP不安全,类似写信;TCP类似打电话,需要连接,有确认机制
-
TCP有拥塞控制和可靠传输机制
- 拥塞控制:指数递增线性增长、快开始
- 可靠传输包括:超时重传、错误校验
-
TCP是严格的客户服务器模式
from socket import *
tcp_client_socket = socket(AF_INET, SOCK_STREAM)
server_ip = input("请输入服务器ip:")
server_port = int(input("请输入服务器port:"))
tcp_client_socket.connect((server_ip, server_port))
send_data = input("请输入要发送的数据:")
tcp_client_socket.send(send_data.encode("gbk"))
recvData = tcp_client_socket.recv(1024)
print('接收到的数据为:', recvData.decode('gbk'))
tcp_client_socket.close()
from socket import *
tcp_server_socket = socket(AF_INET, SOCK_STREAM)
address = ('', 7788)
tcp_server_socket.bind(address)
tcp_server_socket.listen(128)
client_socket, clientAddr = tcp_server_socket.accept()
recv_data = client_socket.recv(1024)
print('接收到的数据为:', recv_data.decode('gbk'))
client_socket.send("thank you !".encode('gbk'))
client_socket.close()
tcp_server_socket.close()
- 区别在于,服务器端会产生两个套接字,分别用于监听和收发数据;
- 这里,服务器端要先收数据(响应),客户端要先发数据(请求);然后互相收发;
-
和UDP的主要区别:
- 严格的客户服务器模式,分离
- 需要链接,而不是简单的告知(IP+端口),保证数据传输的质量
TCP文件下载器
-
客户端代码 from socket import *
def main():
tcp_client_socket = socket(AF_INET, SOCK_STREAM)
server_ip = input("请输入服务器ip:")
server_port = int(input("请输入服务器port:"))
tcp_client_socket.connect((server_ip, server_port))
file_name = input("请输入要下载的文件名:")
tcp_client_socket.send(file_name.encode("utf-8"))
recv_data = tcp_client_socket.recv(1024)
if recv_data:
with open("[接收]"+file_name, "wb") as f:
f.write(recv_data)
tcp_client_socket.close()
if __name__ == "__main__":
main()
-
服务端: from socket import *
import sys
def get_file_content(file_name):
"""获取文件的内容"""
try:
with open(file_name, "rb") as f:
content = f.read()
return content
except:
print("没有下载的文件:%s" % file_name)
def main():
if len(sys.argv) != 2:
print("请按照如下方式运行:python3 xxx.py 7890")
return
else:
port = int(sys.argv[1])
tcp_server_socket = socket(AF_INET, SOCK_STREAM)
address = ('', port)
tcp_server_socket.bind(address)
tcp_server_socket.listen(128)
while True:
client_socket, clientAddr = tcp_server_socket.accept()
recv_data = client_socket.recv(1024)
file_name = recv_data.decode("utf-8")
print("对方请求下载的文件名为:%s" % file_name)
file_content = get_file_content(file_name)
if file_content:
client_socket.send(file_content)
client_socket.close()
tcp_server_socket.close()
if __name__ == "__main__":
main()
-
TCP注意点: -
关闭监听套接字,已经建立连接的accept套接字不会断的哦; -
服务端recv套接字解阻塞有两种方式:客户端关闭(挂电话)、服务端收到数据 -
结合多任务可以实现服务多个用户
|