Python高级内容
闭包
1.闭包的基本的定义
使用外部函数变量的内部函数叫做闭包
2.闭包外部函数
闭包可以保存外部函数内的变量,不会随着外部函数的调用完而销毁,故也会导致外部函数的变量没有及时释放,存在消耗内存的现象发生
#对象引用
# 对象引用: 对象名仅仅只是指向内存地址的变量
def func():
print('running')
func()
func2 = func # func2指向func内存地址
func2()
#闭包实例:
def set_fun(func):
def call_fun():
print("参数值:", func)
return call_fun
#外层函数的执行返回一个内层函数的引用
fun = set_fun("123")
# 内层函数的引用()整个调用了
fun()
def func_out(num1):
#定义一个内部函数
def func_inner(num2):
#修改外部函数的变量
nonlocal num1 # 使用nonlocal来声明 num1为外部变量
num1 = 10
#内部函数使用外部函数变量num1
result = num1 + num2
print(result)
#外部函数返回内部函数,这里就是闭包
return func_inner
f = func_out(1)
f(2)
# case
def config_name(name):
def say_info(info):
print(name + ":" + info)
return say_info
if __name__ == '__main__':
tom = config_name('tom')
tom("你好!")
tom("你好,请问你在吗?收到请回答,谢谢!")
装饰器
1.装饰器的定义
装饰器的基本定义:给已有的函数增加额外的功能函数,本质上是一个闭包函数
2.开放封闭原则
写代码要遵循开放封闭原则,它规定已经实现的功能代码不允许被修改,但可以被扩展。
3.嵌套函数
两个函数的嵌套,外层函数返回内层函数的引用,一般外层都会有参数
4.装饰器的特点
不修改已有函数的源代码
不修改已有函数的调用方式
给已有的函数增加额外的功能
5.相关代码:
def check(fn):
def inner():
print("请先登录。。。")
fn()
return inner
@check
def comment():
print("发表评论")
# 使用语法糖方式来装饰函数 @check 等价于 comment = check(comment)
# comments = check(comment) 等价于comment = inner,通过外层函数获取到内层函数的指向既引用
# comment() 等价于comment() = inner()
# 装饰器的基本雏形
def decorator(fn): # fn:目标函数.
def inner():
'''执行函数之前'''
fn() # 执行被装饰的函数
'''执行函数之后'''
return inner
num =100
def set_fun(func):
def call_fun(args):
args += 1
print("内层的值:", args)
nonlocal func # 修改外层函数参数
func += 1
print("参数值:", func)
global num #修改全局变量,改变了地址的需要加1
num += 1
print("全局值:", num)
return call_fun
fun = set_fun(123)
fun(200)
装饰器的使用
1.实现已有函数执行时间的统计
2.通用装饰器的使用
2.1装饰带有参数的函数
2.2装饰带有返回值的函数
2.3装饰带有不定长参数的函数
相关代码:
#装饰带有返回值的函数
def logging(fn):
def inner(num1,num2):
print("正在努力地计算中")
result = fn(num1,num2)
return result
return inner
@logging
def sum_num(a,b):
result = a + b
return result
result = sum_num(1,3)
print(result)
# 带有不定长参数的函数
def logging(fn):
def inner(*args, **kwargs):
print("正在计算中")
result = fn(*args, **kwargs)
return result
return inner
@logging
def sum_num(*args, **kwargs):
result = 0
for value in args:
result += value
for value in kwargs.values():
result += value
return result
@logging
def substaction(a,b): #通用装饰器部分
result1 = a - b
return result1
result = sum_num(1,2, a=10) #带有不定长参数的部分
print(result)
result1 = substaction(1,23)
print(result1)
#通用装饰基本语法格式:
def logging(fn):
def inner(*args,**kwargs):
result = fn(args, kwargs)
return result
return inner
3.多个装饰器的使用
1.多个装饰器的使用实例
2.多个装饰器的装饰过程是: 离函数最近的装饰器先装饰,然后外面的装饰器再进行装饰,由内到外的装饰过程
3.相关代码:
#多个装饰器的装饰过程是:离函数最近的装饰器先装饰,然后外面的装饰器在进行装饰,由内到外的装饰过程
def make_div(func):
def inner(*args, **kwargs):
return "<div>" + func() + "</div>"
return inner
def make_p(func):
def inner(*args, **kwargs):
return "<p>" + func() + "</p>"
return inner
# content = make_p(content)
# content = make_div(content)
#content = make_div(make_p(content))
@make_div
@make_p
def content():
return "人生精彩!!!"
result = content()
print(result)
4.带有参数的装饰器
1.带有参数的装饰器介绍
2.带有参数的装饰器就是使用装饰器装饰函数的时候可以传入指定参数,语法格式: @装饰器(参数,…)
3.使用带有参数的装饰器,其实是在装饰器外面又包裹了一个函数,使用该函数接收参数,返回是装饰器,因为 @ 符号需要配合装饰器实例使用
相关代码:
def logging(flag):
def decorator(fn):
def inner(num1,num2):
if flag == "+":
print("正在努力进行加法计算")
elif flag == "-":
print("正在进行减法运算")
result = fn(num1,num2)
return result
return inner
# 返回装饰器
return decorator
#使用装饰器函数
@logging("+")
def add(a,b):
result = a + b
return result
@logging("-")
def sub(a,b):
result = a - b
return result
result = add(1,2)
print(result)
result = sub(1,2)
print(result)
5.类装饰器的使用
-
通过一个类来装饰的函数
-
@Check 等价于 comment = Check(comment), 所以需要提供一个init方法,并多增加一个fn参数。
-
要想类的实例对象能够像函数一样调用,需要在类里面使用call方法,把类的实例变成可调用对象(callable),也就是说可以像调用函数一样进行调用。
-
在call方法里进行对fn函数的装饰,可以添加额外的功能。
相关代码:
class check(object): def __init__(self, fn): #初始化操作完成 self.__fn = fn #实现__call__方法,表示对象是一个可调用的对象,可以像调用函数一样进行调用 def __call__(self, *args, **kwargs): #添加装饰功能 print("请先登录。。。") self.__fn() @check def comment(): print("发表评论") comment()
mini_web框架代码:
1.mini_web_22代码:
# 根据不同的地址返回不同的数据
# 装饰器前的test,就是func
# 装饰器后的test,调用是的call_fun
# 通过装饰器传参自动注册字典
# 定义一个空的字典
import re
import json
from pymysql import connect
import logging
import mini_web.my_log
url_dict = dict()
def route(file_path):
def set_fun(func):
def call_fun(*args, **kwargs):
print("添加权限")
return func(*args, **kwargs)
# 打印信息
print("gd:", func)
print("gd:", file_path)
# 自动到字典中注册
url_dict[file_path] = func
return call_fun
return set_fun
# 入口函数简洁
# 一个函数一个功能(网页)
# 如果if..else超过了三个以上可以考虑使用字典
def application(file_path):
# 根据不同的数据返回不同的地址
# 是因为谁处理谁来返回状态
response_line = "HTTP/1.1 200 OK\r\n"
response_head = ""
# 创建一个资源的字典
# 地址:对应页面的函数引用
# url_dict = {"/index.html": index, "/center.html": center,"/login.html":login,"/jd.html":jd}
try:
# 根据不同的地址返回不同的响应内容
response_body = url_dict[file_path]()
except Exception as e:
print("异常", e)
mini_web.my_log.logger.error(e)
# 网页不存在404
response_line = "HTTP/1.1 404 not found\r\n"
response_body = "not page is show!"
return response_line, response_head, response_body
##########################################上面就是框架####################################
# @route("/index.html")
# def index():
# # 打开前端的界面
# with open("/home/kaiqi/PycharmProjects/pythonProject/templates/index.html") as f:
# content = f.read()
# return content
#
# @route("/center.html")
# def index():
# # 打开前端的界面
# with open("/home/kaiqi/PycharmProjects/pythonProject/templates/center.html") as f:
# content = f.read()
# return content
#
# @route("/home/kaiqi/PycharmProjects/pythonProject/templates/update.html")
# def index():
# # 打开前端的界面
# with open("templates.") as f:
# content = f.read()
# return content
#写一个对应的网页的处理函数函数
def mysql_conn():
conn = connect(host="192.168.66.131", port=3306, database="stock_db", user="root", password="root", charset="utf8")
#获取到cursor对象
cs1 = conn.cursor()
cs1.execute("select * from info;")
#得到数据库数据
data = cs1.fetchall()
cs1.close()
cs2 = conn.cursor()
cs2.execute("select info.code, info.short, info.chg, info.turnover, info.price, info.highs, focus.note_info from info inner join focus on info.id = focus.info_id;")
data2 = cs2.fetchall()
cs2.close()
conn.close()
return data, data2
"""前后端不分离写法"""
"==============================================================================================="
@route("/index.html")
def index():
# 打开前端的界面
with open("/home/kaiqi/PycharmProjects/pythonProject/templates/index.html") as f:
content = f.read()
test1,test2 = mysql_conn()
for temp in test1:
mini_web.my_log.logger.info(temp)
#拼接数据库的数据用于在前端显示
row_str = """
<tr>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td>
<input type="button" value="添加" id="toAdd" name="toAdd" systemidvaule="%s">
</td>
</tr>
"""
table_str = ""
for temp in test1:
table_str += row_str % (temp[0], temp[1], temp[2], temp[3], temp[4], temp[5], temp[6], temp[7], temp[1])
new_content = re.sub("\{%content%\}", table_str, content)
# 返回
return new_content
#
# @route('/center.html')
# def center():
# with open("/home/kaiqi/PycharmProjects/pythonProject/templates/center.html") as f:
# content = f.read()
# test1, test2 = mysql_conn()
#
# # for temp in test2:
# # print(temp)
#
# #拼接数据库数据用于个人数据的展示
# row_str = """
# <tr>
# <td>%s</td>
# <td>%s</td>
# <td>%s</td>
# <td>%s</td>
# <td>%s</td>
# <td>%s</td>
# <td>%s</td>
# <td>
# <a type="button" class="btn btn-default btn-xs" href="/update/%s.html"> <span class="glyphicon glyphicon-star" aria-hidden="true"></span> 修改 </a>
# </td>
# <td>
# <input type="button" value="删除" id="toDel" name="toDel" systemidvaule="%s">
# </td>
# </tr>
# """
# table_str = ""
# for temp in test2:
# table_str += row_str % (temp[0], temp[1], temp[2], temp[3], temp[4], temp[5], temp[6], temp[0], temp[0])
#
# new_content = re.sub("\{%content%\}", table_str, content)
# # 返回
# return new_content
# #
# @route("/add/000007.html")
# def add():
# #直接把数据添加到数据库当中,从数据库中得到数据,
# conn = connect(host="192.168.66.131", port=3306, database="stock_db", user="root", password="root", charset="utf8")
# cs1 = conn.cursor()
# cs1.execute("""insert into focus(info_id) (select id from info where code = "0000007");""")
# conn.commit()
# cs1.close()
# conn.close()
# return "添加成功"
#无需拼接前端数据,只需要返回前端发来的请求数据即可,其中前端对应的数据由ajax来返回给对应的后端
"""前后端分离的写法"""
"==============================================================================================="
@route("/center.html")
def center():
with open("/home/kaiqi/PycharmProjects/pythonProject/templates2/center.html") as f:
content = f.read()
return content
@route("/center_data.html")
def center_data():
test3,test4 = mysql_conn()
json_list = list()
for temp in test4:
#遍历每一行数据转成字典
#consist of dict_data ,then append list
json_dict = dict()
json_dict['code'] = temp[0]
json_dict['short'] = temp[1]
json_dict['chg'] = temp[2]
json_dict['turnover'] = temp[3]
json_dict['price'] = str(temp[4])
json_dict['highs'] = str(temp[5])
json_dict['note_info'] = str(temp[6])
json_list.append(json_dict)
print("json_list:", json_list)
json_str = json.dumps(json_list)
print("jso_str:", json_str)
return json_str
"""
json.dumps函数把字典转成json字符串
函数的第一个参数表示要把指定对象转成json字符串
参数的第二个参数ensure_ascii=False表示不使用ascii编码,可以在控制台显示中文。
响应头添加Content-Type表示指定数据的编码格式
"""
2.面向对象服务器代码:
# 创建一个web服务器
# 1.创建一个tcp服务器
# 1.1 初始化socket
# 1.2 绑定及复用
# 1.3 监听
# 2.循环接收游览器的请求
# 3.处理游览器的请求
# 3.1 得到请求的地址
# 3.2根据不同的请求地址返回不同的请求数据
# 4.关闭
import socket
import threading
import mini_web.mini_web_22
class WebServer(object):
def client_exec(self, client):
"""
客户端的处理
:param client: 与之对应的scoket
:return: none
"""
# 3.1 得到请求的地址
# 3.2根据不同的请求地址返回不同的请求数据
# 3.1 得到请求的地址
# 得到请求的数据
data = client.recv(1024)
# 必须要判断,千万不要相信别人给你传的数据
if data:
# 说明有数据
# 处理数据
# 解码的数据
decode_data = data.decode("utf-8")
print(decode_data)
# GET /index.html HTTP/1.1
split_data = decode_data.split(" ", maxsplit=2)
# print("切割的数据:",split_data)
# 地址
# 这个有可能出错
try:
file_path = split_data[1]
print("地址:", file_path)
# 如果是/那么定位到/index.html
if file_path == "/":
file_path = "/index.html"
except Exception as e:
print("异常", e)
# 关闭退出
client.close()
return
else:
# 没有数据
# 返回关闭
client.close()
return
# 根据服务器资源的特性进行分类,如果是动态的一个一个处理,但是如果是静态统一处理,
# 静态的比如:图片,视频,音频,js,css
# 一般如果后缀是.html这个网页内容,这个能改就是动态的处理
# 其他都是静态的
if file_path.endswith(".html"):
# 说明是动态的资源,一个一个处理
# 登陆
# 返回 网页的内容
# response_line = "HTTP/1.1 200 OK\r\n"
response_head = ""
response_empty = "\r\n"
# 所有的动态资源的逻辑都让mini_webb文件处理
# 根据不同的地址返回不同的数据n
response_line,response_head,response_body = mini_web.mini_web_22.application(file_path)
response_content = response_line + response_head + response_empty + response_body
client.send(response_content.encode("utf-8"))
else:
try:
# 防止静态资源不存在
# 说明是静态的资源
# 统一的处理
# 统一使用二进制读取进行拼接返回 数据
response_line = "HTTP/1.1 200 OK\r\n"
response_head = ""
response_empty = "\r\n"
# file_path这个是静态资源的路径
with open("/home/kaiqi/PycharmProjects/pythonProject/static%s" % file_path, 'rb') as f:
response_body = f.read()
response_content = response_line.encode("utf-8") + response_head.encode(
"utf-8") + response_empty.encode(
"utf-8") + response_body
client.send(response_content)
except Exception as e:
print("异常:", e)
# 返回 网页的内容
response_line = "HTTP/1.1 404 not found\r\n"
response_head = ""
response_empty = "\r\n"
response_body = ""
response_content = response_line + response_head + response_empty + response_body
client.send(response_content.encode("utf-8"))
# 关闭
client.close()
def run_server(self):
# 2.循环接收游览器的请求
while True:
client, address = self.tcp_server.accept()
# 处理浏览器请求
# self.client_exec(client) # 这个代码有可能执行的时间很长
# 多任务版
threading.Thread(target=self.client_exec, args=(client,)).start()
# 3.关闭
tcp_server.close()
def __init__(self):
# 初始化套接字服务器
# 1.创建套接字
self.tcp_server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 2.绑定端口与复用端口
self.tcp_server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.tcp_server.bind(("", 8080))
# 3.被动模式
self.tcp_server.listen(128)
# 入口函数必须简洁
def main():
"""创建一个web服务器"""
# 初始化
server = WebServer()
# 开启服务
server.run_server()
if __name__ == '__main__':
main()
3.my_Log代码
import logging
# 第一步,创建一个logger
logger = logging.getLogger()
#总开发不要动
logger.setLevel(logging.DEBUG) # Log等级总开关
# 第二步,创建一个handler,用于写入日志文件
#写入文件的地址及其2文件名可以进行变更
logfile = './log.txt'
fh = logging.FileHandler(logfile, mode='a') # open的打开模式这里可以进行参考
fh.setLevel(logging.WARNING) # 输出到file的log等级的开关
# 第三步,再创建一个handler,用于输出到控制台
ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG) # 输出到console的log等级的开关
# 第四步,定义handler的输出格式
formatter = logging.Formatter("%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s")
fh.setFormatter(formatter)
ch.setFormatter(formatter)
# 第五步,将logger添加到handler里面
logger.addHandler(fh)
logger.addHandler(ch)
property属性
1.property属性定义
2.装饰器方式
3.装饰器代码
"""=======================================装饰器================================================================"""
"""
property属性:负责吧一个方法当做属性进行使用,这种做可以简化代码的使用
装饰器方法: 其中装饰器的property属性修饰的方法名一定要一样
类属性方法:第一个参数是获取属性时要执行的方法
第二个参数是设置属性时要执行的方法
"""
#装饰器方法的使用
# class Person(object):
# def __init__(self):
# self.__age =0
# #装饰器方法把age方法当做属性使用,表示获取属性是会执行下面的修饰方法
# @property
# def age(self):
# return self.__age
#
# #把age方法当做属性使用,表示当设置属性时会执行下面的修饰方法
# @age.setter
# def age(self, new_age):
# if new_age >150:
# print("成精了")
# else:
# self.__age = new_age
# #创建person
# p = Person()
# print(p.age)
#
#
# p.age = 100
# print(p.age)
#
# p.age = 1000
#类属性方法
# class Person(object):
# def __init__(self):
# self.__age =0
#
# def get_age(self):
# return self.__age
#
# def set_age(self,new_age):
# if new_age >150:
# print("成精了")
# else:
# self.__age = new_age
# age = property(get_age, set_age)
# #第一个参数是获取属性是要执行的方法
# #第二个参数是设置属性时要执行的方法
#
# p = Person()
# print(p.age)
#
#
# p.age = 100
# print(p.age)
#
# p.age = 1000
上下文管理器
1.with语句的使用
with语句执行完成以后会自动调用关闭文件的操作,即使是出现异常情况也会关闭相应的文件操作
2.上下文管理
with功能的实现是因为上下文管理器的中的___enter__()方法和__exit__()方法"“”
3.相关代码
"""=======================================上下文管理器================================================================"""
"""with语句和上下文管理器"""
#with语句执行完成以后会自动调用关闭文件的操作,即使是出现异常情况也会关闭相应的文件操作
# with open("1.txt", "w") as f:
# #读取文件内容
# f.write("hello world!")
"""上下文管理器:
with功能的实现是因为上下文管理器的中的__enter__()方法和__exit__()方法"""
# class file(object):
# #初始化方法
# def __init__(self, file_name , file_model):
# self.file_name = file_name
# self.file_model = file_model
#
# #上文方法
# def __enter__(self):
# print('进入上下文啦')
#
# self.file = open(self.file_name, self.file_model)
# return self.file
#
# #下文方法
# def __exit__(self, exc_type, exc_val, exc_tb):
# print("进入下文方法")
# self.file.close()
#
# if __name__ == '__main__':
# with file("1.txt", "r") as f:
# content = f.read()
#
# print(content)
#上下文管理器的另外一种实现方式
#导入装饰器
# from contextlib import contextmanager
#
# #装饰器函数,让其成为一个上下文管理器对象
# @contextmanager
# def my_open(file_name,file_mode):
# try:
# file = open(file_name, file_mode)
# #yeild之 前的代码好比上文方法
# yield file
# except Exception as e:
# print(e)
#
# finally:
# print("over")
# #yield下面的好像好比下文方法
# file.close()
# with my_open('out.txt', 'w') as f:
# f.write("hello , the simplest context manager")
生成器
1.生成器的介绍
根据程序员制定的规则循环生成数据,当条件不成立时则生成数据结束。数据不是一次性全部生成处理,而是使用一个,再生成一个,可以节约大量的内存。
2.创建生成器的方式
生成器推导式
yield关键字
3. 生成器的使用场景
斐波拉契数列
4.相关代码
"""=======================================生成器================================================================"""
"""生成器的创建方式
1。数据不是一次性生成的而是使用一个,在生成一个,主要用来节省内存的作用"""
#生成器
#创建生成器
# my_generator = (i * 2 for i in range(5))
# print(my_generator)
# #
# # #next获取下一个生成器的值
# value = next(my_generator)
# print("next generator vvalue", value)
# # #遍历生成器
# for value in my_generator:
# print('for generator:', value)
#next 函数获取生成器中的下一个值
#for 循环遍历生成器中的每一个值
# def my_generator(n):
# for i in range(n):
# print("开始生成")
# yield i
# print("finish once")
# if __name__ == '__main__':
# g = my_generator(2)
# # for_loop inner will be process automatic stop ineration abnormal ,is's first code way;
# # for i in g:
# # print(i)
#
# #获取生成器中下一个值
# # result = next(g)
# # print(result)
#
# #while loop inner do not process this abnormal,it needs to manual append similar operation
# while True:
# try:
# result = next(g)
# print(result)
# except StopAsyncIteration as e:
# break
#生成器的应用场景
# def fib(num):
# a = 0
# b = 1
# #记录生成的fib数字下表
# current_index = 0
# while current_index <num :
# result = a
# a, b = b, a+b
# current_index +=1
# yield result
# fibs = fib(5)
# for value in fibs:
# print(value)
深拷贝和浅拷贝
1.浅拷贝
shallow copy:只对可变类型的第一层对象进行copy,对copy的对象开辟新的内存空间进行存储,并不会copy对象内部的子对象
1.1对不可变类型进行:浅拷贝操作,不会给拷贝的对象新开辟新的内存空间而是拷贝了这个对象的引用
1.2 可变类型进行:浅拷贝,只对可变类型的第一层对象进行copy,对copy的对象开辟新的内存空间进行存储,并不会copy对象内部的子对象
2.深拷贝
deep copy:只要发现对象有可变类型就会对该对象到最后一层可变类型的每一层对象进行拷贝,对每一层的对象都会开辟新的内存空间进行存储
1.1 不可变类型深拷贝:如果子对象没有可变类型则只是拷贝这个对象的引用,否则对该对象最后一个可变类型进行拷贝,且对每一层的对象都会开辟新的内存空间进行存储
1.2 可变类型深拷贝:对该对象最后一个可变类型的每一层对象进行拷贝,每一个拷贝的对象都会开辟新的内存空间进行存储
3.区别
浅拷贝最多拷贝对象的一层
深拷贝可能拷贝对象的多层
4.相关代码
#shallow copy
#对不可变类型进行浅拷贝操作 数字 字符串 元祖
import copy
#不可变类型
# a1 = 123123
# b1 = copy.copy(a1)
# print("id_a:",id(a1))
# print("id_b:",id(b1))
#
#浅拷贝可变类型
# a2 = [1, 2, [4, 5]]
# b2 = copy.copy(a2)
# #浅拷贝父类结果
# print("id_a2:", id(a2))
# print("id_b2:", id(b2))
#
# #浅拷贝子类结果
# print("子对象结果a2:",id(a2[2]))
# print("子对象结果b2",id(b2[2]))
#
#
# #修改数据验证浅拷贝
# #修改第一层数据
# a2[0]= 11
# print(a2)
# print(b2)
#
# #修改子对象,验证浅拷贝只会对父对象进行copy
# a2[2][0] = 5
# print(a2)
# print(b2)
#子对象或内层进行修改后浅拷贝的结果也跟着更改说明并没有创建新的内存空间,只是拷贝了这个对象的引用,既两者指向同一内存地址
#深拷贝
#不可变类型
# 注意: 元组里面要是有可变类型对象,发现对象有可变类型就会该对象到最后一个可变类型的每一层对象进行拷贝
# a3 = (1, ["lisi"])
# b4 = copy.deepcopy(a3)
# print("ida3:", id(a3))
# print("idb4:", id(b4))
# # 元组里面的可变类型子对象也会进行拷贝
# print(id(a3[1]))
# print(id(b4[1]))
# print(a3)
# print(b4)
#可变类型
# a4 = [1, 2, ["李四", "王五"]]
# b5 = copy.deepcopy(a4)
# print("可变类型深拷贝a4:",id(a4))
# print("可变类型深拷贝b5:", id(b5))
# print("内层a4:", id(a4[2]))
# print("内层b5:",id(b5[2]))
#
# #修改原始数组的值验证深拷贝结果
# a4[2][0] = "你好"
# 因为列表的内存地址不同,所以数据不会收到影响
# print(a4)
# print(b5)