包
包: # 包是一系列模块文件的结合体,表现形式是文件夹,该文件夹内部通常会包含一个__init__.py文件,本质上还是一个模块
包呢,就是前两篇博客中提到的,模块的四种表现形式中的第三种 # 把一系列模块(.py文件)组合到一起的文件夹(包)
下文呢,也将把包与模块前面的模块知识对比展开。
首先,复习下执行文件导入模块发生的一些事
""" 1.先产生一个执行文件的名称空间 2.创建模块文件的名称空间 3.执行模块文件中的代码 将产生的名字放入模块的名称空间中 4.在执行文件中拿到一个指向模块名称空间的名字 """
导入模块的写法呢,也跟导入模块基本一致(包本身也是模块的一种嘛)
from dir.dir1 import p # 从执行文件同级(或者是sys.path中路径下)的dir目录下的dir1目录导入模块p(文件夹)
准备工作:创建出如图所示的文件目录,并添加内容
def hello(): return "hello ya, i'm from dir/dir1/p"
from dir.dir1 import p # 导入包 print(p.hello()) # hello ya, i'm from dir/dir1/p
上述过程发生了以下几件事情
""" 首次导入包: 1.先产生一个执行文件的名称空间 2.创建包下面的__init__.py文件的名称空间 3.执行包下面的__init__.py文件中的代码 将产生的名字放入包下面的__init__.py文件名称空间中 4.在执行文件中拿到一个指向包下面的__init__.py文件名称空间的名字 """
那么为什么要分包呢?
""" 当你作为包的设计者来说 1.当模块的功能特别多的情况下 应该分文件管理 2.每个模块之间为了避免后期模块改名的问题 你可以使用相对导入(包里面的文件都应该是被导入的模块,不考虑作为执行文件,也就无所谓相对路径的问题了) 站在包的开发者来说 如果使用绝对路径来管理的自己的模块 那么他只需要永远以包的路径为基准依次导入模块(一般会保证调用方法不变) """
导包时的一些注意点
""" 站在包的使用者 你必须得将包所在的那个文件夹路径添加到system path中(******) python2如果要导入包 包下面必须要有__init__.py文件 python3如果要导入包 包下面没有__init__.py文件也不会报错 当你在删程序不必要的文件的时候 千万不要随意删除__init__.py文件 在导入语句中 .号的左边肯定是一个包(文件夹) ----> 这句话是对的 """
更改模块名不影响原使用方式举例(注意他们对应的目录层级关系)
def func1(): print("dir/dir1/p/下的 func1") def func2(): print("dir/dir1/p/下的 func2") def func3(): print("dir/dir1/p/下的 func3")
def hello(): return "hello ya, i'm from dir/dir1/p" from dir.dir1.p.my_model import *
from dir.dir1 import p print(p.hello()) # hello ya, i'm from dir/dir1/p p.func1() # dir/dir1/p/下的 func1 p.func2() # dir/dir1/p/下的 func2 p.func3() # dir/dir1/p/下的 func3
此时,如果我觉得之前取的名字 my_model.py 太low了,想换成 print_model.py ,那么我只需要把 # my_model.py 的名字改成 print_model.py ,并把 # dir/dir1/p/__init__.py的 from dir.dir1.p.my_model import * 改成 from dir.dir1.p.print_model import * 即可,原来调用该模块的 test2.py 文件无需任何改动(试想如果你的test2.py里有很多 my_model.func1这种写法,那你岂不是要把所有用到的地方都改成 print_model.func1?如果有几十个地方引用了,亦或者是好多不同的文件都用到了,那太可怕了)
logging模块
logging日志: # 可以记录软件的运行信息,用户的操作行为,监视软件运行是否异常。
日志的等级
''' 日志分为五个等级: 等级 权重 大致用途 debug 10 记录一些程序运行中的信息 info 20 记录一些操作信息 warning 30 记录一些警告信息 error 40 记录一些错误信息(可能导致程序报错) critical 50 记录一些严重的错误信息(可能导致程序停止运行) '''
简单的日志案例
import logging logging.basicConfig(filename='access.log', format='%(asctime)s - %(name)s - %(levelname)s -%(module)s: %(message)s', datefmt='%Y-%m-%d %H:%M:%S %p', level=30, ) logging.debug('debug日志') # 10 logging.info('info日志') # 20 logging.warning('warning日志') # 30 logging.error('error日志') # 40 logging.critical('critical日志') # 50
''' logging.basicConfig()函数中可通过具体参数来更改logging模块默认行为,可用参数有: filename:用指定的文件名创建FiledHandler,这样日志会被存储在指定的文件中。 filemode:文件打开方式,在指定了filename时使用这个参数,默认值为“a”还可指定为“w”。 format:指定handler使用的日志显示格式。 datefmt:指定日期时间格式。 level:设置rootlogger(后边会讲解具体概念)的日志级别 stream:用指定的stream创建StreamHandler。可以指定输出到sys.stderr,sys.stdout或者文件(f=open(‘test.log’,’w’)),默认为sys.stderr。若同时列出了filename和stream两个参数,则stream参数会被忽略。 '''
发现上述案例运行后的日志文件中有乱码,且只出现在了文件里,似乎格式也不是我想要的,那么怎么解决呢?
首先,给你介绍几个对象
""" 1.logger对象:负责产生日志 2.filter对象:过滤日志(了解) 3.handler对象:控制日志输出的位置(文件/终端) 4.formatter对象:规定日志内容的格式 """
下面来写一个小案例
''' 注意程序执行结束多出来的两个文件(a1.log, a2.log)的内容与控制台的内容 ''' import logging # 1.logger对象:负责产生日志 logger = logging.getLogger('日志记录') # 2.filter对象:过滤日志(了解) # 用不太到 # 3.handler对象:控制日志输出的位置(文件/终端) hd1 = logging.FileHandler('a1.log', encoding='utf-8') # 输出到文件中,传入第二个参数 encoding='utf-8' 防止中文乱码 hd2 = logging.FileHandler('a2.log', encoding='utf-8') hd3 = logging.StreamHandler() # 输出到终端 # 4.formatter对象:规定日志内容的格式 fm1 = logging.Formatter( fmt='%(asctime)s - %(name)s - %(levelname)s -%(module)s: %(message)s', datefmt='%Y-%m-%d %H:%M:%S %p', ) fm2 = logging.Formatter( fmt='%(asctime)s - %(name)s: %(message)s', datefmt='%Y-%m-%d', ) # 5.给logger对象绑定handler对象(绑定不同的输出文件、终端) logger.addHandler(hd1) logger.addHandler(hd2) logger.addHandler(hd3) # 6.给handler绑定formatter对象 (给他们绑定不同的日志格式) hd1.setFormatter(fm1) hd2.setFormatter(fm2) hd3.setFormatter(fm1) # 7.设置日志等级(debug、info、warning、err、critical),低于这个等级的信息将被过滤(舍弃) logger.setLevel(20) # info # 8.记录日志 logger.debug('debug日志信息') logger.info('info日志信息') logger.error('error日志信息') # -------------* 控制台输出信息 *-------------------- # 2019-07-19 19:53:30 PM - 日志记录 - INFO -test2: info日志信息 # 2019-07-19 19:53:30 PM - 日志记录 - ERROR -test2: error日志信息
从上面案例中可以得知,改变日志格式的要点就是 formatter对象,那么他有哪些格式的参数呢?
''' format参数中可能用到的格式化串: %(name)s Logger的名字 %(levelno)s 数字形式的日志级别 %(levelname)s 文本形式的日志级别 %(pathname)s 调用日志输出函数的模块的完整路径名,可能没有 %(filename)s 调用日志输出函数的模块的文件名 %(module)s 调用日志输出函数的模块名 %(funcName)s 调用日志输出函数的函数名 %(lineno)d 调用日志输出函数的语句所在的代码行 %(created)f 当前时间,用UNIX标准的表示时间的浮 点数表示 %(relativeCreated)d 输出日志信息时的,自Logger创建以 来的毫秒数 %(asctime)s 字符串形式的当前时间。默认格式是 “2003-07-08 16:49:45,896”。逗号后面的是毫秒 %(thread)d 线程ID。可能没有 %(threadName)s 线程名。可能没有 %(process)d 进程ID。可能没有 %(message)s用户输出的消息 '''
logging配置字典
上面的配置还是有些麻烦,那有没有现成配置可以让我们轻松点呢?哎,就是logging配置字典,算了直接上案例写成一个通用方法吧
# ************************************ 日志配置字典 ************************************************** ''' -------------------------------------------- ------------* 需要自定义的配置 *------------- -------------------------------------------- ''' # 定义三种日志输出格式 开始 standard_format = '[%(asctime)s][%(threadName)s:%(thread)d][task_id:%(name)s][%(filename)s:%(lineno)d]' \ '[%(levelname)s][%(message)s]' # 其中name为getlogger指定的名字 simple_format = '[%(levelname)s][%(asctime)s][%(filename)s:%(lineno)d]%(message)s' # 定义日志输出格式 结束 # 定义日志文件的存放目录与文件名(一般视情况选择下面的两个即可) logfile_dir = os.path.dirname(os.path.dirname(__file__)) # log文件的目录 (执行文件starts.py 在bin目录下) # logfile_dir = os.path.abspath(__file__) # log文件的目录 (执行文件starts.py 在项目根目录下) # 拼上 log 文件夹 logfile_dir = os.path.join(logfile_dir, 'log') logfile_name = 'ATM_Shop_Cart.log' # log文件名 # 如果不存在定义的日志目录就创建一个 if not os.path.isdir(logfile_dir): os.mkdir(logfile_dir) # log文件的全路径 logfile_path = os.path.join(logfile_dir, logfile_name) # 像日志文件的最大限度、个数限制,日志过滤等级等也可以在下面的对应地方限制 ''' -------------------------------------------- --------------* log配置字典 *--------------- -------------------------------------------- ''' # log配置字典 LOGGING_DIC = { 'version': 1, 'disable_existing_loggers': False, 'formatters': { 'standard': { 'format': standard_format }, 'simple': { 'format': simple_format }, }, 'filters': {}, # 一般用不到过滤,所以就空在这里了 'handlers': { 'console': { 'level': 'DEBUG', 'class': 'logging.StreamHandler', # 打印到屏幕 'formatter': 'simple' }, # 打印到文件的日志,收集info及以上的日志 'default': { 'level': 'DEBUG', 'class': 'logging.handlers.RotatingFileHandler', # 保存到文件 'formatter': 'standard', 'filename': logfile_path, # 日志文件 'maxBytes': 1024 * 1024 * 5, # 日志大小上限 5M,超过5M就会换一个日志文件继续记录 'backupCount': 5, # 日志文件的数量上限 5个,超出会把最早的删除掉再新建记录日志 'encoding': 'utf-8', # 日志文件的编码,再也不用担心中文log乱码了 }, }, 'loggers': { # logging.getLogger(__name__)拿到的logger配置 '': { 'handlers': ['default', 'console'], # 这里把上面定义的两个handler都加上,即log数据既写入文件又打印到屏幕 # 'handlers': ['default'], # ------------- 正式上线的时候改成这个,取消控制台打印,节省资源 ------------- 'level': 'DEBUG', 'propagate': True, # 向上(更高level的logger)传递 }, }, }
怎么运用到实际项目中去呢?很简单,看下面
在配置文件中放入logging配置字典
import os import sys BASE_DIR = os.path.dirname(os.path.dirname(__file__)) sys.path.append(BASE_DIR) # ************************************ 日志配置字典 ************************************************** ''' -------------------------------------------- ------------* 需要自定义的配置 *------------- -------------------------------------------- ''' # 定义三种日志输出格式 开始 standard_format = '[%(asctime)s][%(threadName)s:%(thread)d][task_id:%(name)s][%(filename)s:%(lineno)d]' \ '[%(levelname)s][%(message)s]' # 其中name为getlogger指定的名字 simple_format = '[%(levelname)s][%(asctime)s][%(filename)s:%(lineno)d]%(message)s' # 定义日志输出格式 结束 # 定义日志文件的存放目录与文件名(一般视情况选择下面的两个即可) logfile_dir = os.path.dirname(os.path.dirname(__file__)) # log文件的目录 (执行文件starts.py 在bin目录下) # logfile_dir = os.path.abspath(__file__) # log文件的目录 (执行文件starts.py 在项目根目录下) # 拼上 log 文件夹 logfile_dir = os.path.join(logfile_dir, 'log') logfile_name = 'ATM_Shop_Cart.log' # log文件名 # 如果不存在定义的日志目录就创建一个 if not os.path.isdir(logfile_dir): os.mkdir(logfile_dir) # log文件的全路径 logfile_path = os.path.join(logfile_dir, logfile_name) # 像日志文件的最大限度、个数限制,日志过滤等级等也可以在下面的对应地方限制 ''' -------------------------------------------- --------------* log配置字典 *--------------- -------------------------------------------- ''' # log配置字典 LOGGING_DIC = { 'version': 1, 'disable_existing_loggers': False, 'formatters': { 'standard': { 'format': standard_format }, 'simple': { 'format': simple_format }, }, 'filters': {}, # 一般用不到过滤,所以就空在这里了 'handlers': { 'console': { 'level': 'DEBUG', 'class': 'logging.StreamHandler', # 打印到屏幕 'formatter': 'simple' }, # 打印到文件的日志,收集info及以上的日志 'default': { 'level': 'DEBUG', 'class': 'logging.handlers.RotatingFileHandler', # 保存到文件 'formatter': 'standard', 'filename': logfile_path, # 日志文件 'maxBytes': 1024 * 1024 * 5, # 日志大小上限 5M,超过5M就会换一个日志文件继续记录 'backupCount': 5, # 日志文件的数量上限 5个,超出会把最早的删除掉再新建记录日志 'encoding': 'utf-8', # 日志文件的编码,再也不用担心中文log乱码了 }, }, 'loggers': { # logging.getLogger(__name__)拿到的logger配置 '': { 'handlers': ['default', 'console'], # 这里把上面定义的两个handler都加上,即log数据既写入文件又打印到屏幕 # 'handlers': ['default'], # ------------- 正式上线的时候改成这个,取消控制台打印,节省资源 ------------- 'level': 'DEBUG', 'propagate': True, # 向上(更高level的logger)传递 }, }, }
在公共函数中写成公用方法
import logging.config from conf import settings # 获取日志对象 def get_logger(type_name): logging.config.dictConfig(settings.LOGGING_DIC) logger = logging.getLogger(type_name) return logger
在要用到的文件中引入使用(一般是在三层架构的接口层)
from db import db_handler from lib import common # 1.导入日志方法 from core import src # 2.获取日志对象,这里可以给整个文件使用 user_logger = common.get_logger('user_logger') def register(username, pwd, balance=15000): pwd = common.get_md5(pwd) # common中自定义的加密方法 user_dict = { "username": username, "pwd": pwd, 'balance': balance, 'flow': [], 'shop_cart': {}, 'lock': False, } # 3.记录日志 user_logger.info(f"{username}用户注册成功,初始余额{balance}元。") return db_handler.save(user_dict)
生成日志文件案例
[2019-07-23 11:22:14,291][MainThread:9424][task_id:user_logger][user_interface.py:39][INFO][tank用户登录成功。] [2019-07-23 11:23:54,501][MainThread:14592][task_id:user_logger][user_interface.py:39][INFO][tank用户登录成功。] [2019-07-23 11:24:08,013][MainThread:14592][task_id:user_logger][user_interface.py:39][INFO][tank用户登录成功。] [2019-07-23 11:24:43,051][MainThread:11864][task_id:user_logger][user_interface.py:39][INFO][tank用户登录成功。]
小提示:项目正式上线后要把控制台的日志打印关掉,占资源(在配置字典片结尾处我有注释哦,在开发阶段可以利用控制台打印日志来调试)
hashlib模块
模块简介: # hashlib提供了常见的摘要算法,如MD5,SHA1等等。
那什么又是摘要算法呢?
# 摘要算法又称哈希算法、散列算法。 # 它通过一个函数,把任意长度的数据转换为一个长度固定的数据串(通常用16进制的字符串表示)。
应用场景: # 密码加密,文件是否被篡改检验
haslib模块他有很多算法
""" 1.不用的算法 使用方法是相同的 密文的长度越长 内部对应的算法越复杂 但是 1.时间消耗越长 2.占用空间更大 通常情况下使用md5算法 就可以足够了 """
同样的字符串,不管分成几次传入,他的结果都是相同的
import hashlib # 传入的内容 可以分多次传入 只要传入的内容相同 那么生成的密文肯定相同 md = hashlib.md5() # md.update(b'areyouok?') # print(md.hexdigest()) # # 408ac8c66b1e988ee8e2862edea06cc7 md.update(b'are') md.update(b'you') md.update(b'ok?') print(md.hexdigest()) # 408ac8c66b1e988ee8e2862edea06cc7
同样的字符串加密后的结果是一样的,这也就存在着被撞库(用密码多次加密后比对试出密码)的风险,所有就出现了加盐加密(用户输入一段,与程序中的一段结合加密)
import hashlib md = hashlib.md5() # 公司自己在每一个需要加密的数据之前 先手动添加一些内容 md.update(b'oldboy.com') # 加盐处理 md.update(b'hello') # 真正的内容 print(md.hexdigest()) # fdaf65bc14953d6cc27eab51d862bc33
上面的加盐处理一旦盐被泄露,整个网站仍会初入危险中,那么就出现了动态加盐(使用每个用户的用户名某几位作为盐,从而盐的值就不固定了)这种操作(个人的骚想法是用用户的密码某几位作为动态盐,结合密码做校验)
# 动态加盐 import hashlib def get_md5(data): md = hashlib.md5() md.update('加盐'.encode('utf-8')) # 这里的加盐可以是用户名前一位后一位或者某几位组成(保证了盐不一样,动态的额) md.update(data.encode('utf-8')) return md.hexdigest() password = input('password>>>:') res = get_md5(password) print(res) # password>>>:123 # f9eddd2f29dcb3136df2318b2b2c64d3
文件是否篡改校验小案例
def two_file_diff(file1_path, file2_path): import hashlib import os if os.path.exists(file1_path) and os.path.exists(file2_path): file1_size = os.path.getsize(file1_path) # 通过 os.path.getsize 获取到文件的大小 file2_size = os.path.getsize(file2_path) # print(file1_size/1024/1024, file2_size/1024/1024) # 将单位 Bytes --> MB # 2364.034345626831 2364.343195915222 # 我往另一个压缩包里扔了东西 if file1_size != file2_size: return "两个文件不一致,可能存在着被修改的风险,请您重新下载." else: with open(file1_path, 'rb') as file1: # b模式不需要也不能指定 encoding file1_md = hashlib.md5() # 分段获取到该文件的一些内容,来比较,这个分成哪几段可以随机分,不然被猜到了就不太好了 file1_md.update(file1.read(3).encode('utf-8')) # 注意这个read的字节数不要超过文件大小。。。,我这里是知道这个文件大,就随便取了 file1.seek(file1_size*3//10, 0) # 取整个文件近3/10处的一段字符 file1_md.update(file1.read(2).encode('utf-8')) # 20个字符 file1.seek(file1_size*6//7, 0) # file1_md.update(file1.read(1).encode('utf-8')) # 片段都放进来了,开始计算 file1_md = file1_md.hexdigest() # 注意点是这里取的位置和字节数都必须一致,这样才能比较是否相同。。。 不然,你取的都不一样加密出来当然不一样咯 with open(file2_path, 'rb') as file2: file2_md = hashlib.md5() # 分段获取到该文件的一些内容,来比较,这个分成哪几段可以随机分,不然被猜到了就不太好了 file2_md(file2.read(3).encode('utf-8')) # 注意这个read的字节数不要超过文件大小。。。,我这里是知道这个文件大,就随便取了 file2.seek(file2_size*3//10, 0) # 取整个文件近3/10处的一段字符 file2_md(file2.read(2).encode('utf-8')) # 20个字符 file2.seek(file2_size*6//7, 0) # 其实这个公式里的 file1_size file2_size 无所谓,前面已经判断他们相等了才进到这里的 file2_md(file2.read(1).encode('utf-8')) file2_md = file2_md.hexdigest() if file1_md == file2_md: return "文件并无篡改痕迹,您可安心使用。" else: return "两个文件不一致,可能存在着被修改的风险,请您重新下载." else: if not os.path.exists(file1_path): return f"文件不存在,请检查路径是否有误:{file1_path}" elif not os.path.exists(file2_path): return f"文件不存在,请检查路径是否有误:{file2_path}" else: return f"文件不存在,请检查路径是否有误:{file1_path} {file1_path}" file1_path = r'F:\python听课笔记\测试\python上课视频.zip' # 你要比较的文件1 file2_path = r'F:\python听课笔记\测试\python上课视频 - 副本.zip' # 你要比较的文件2 # import time # start_time = time.time() res = two_file_diff(file1_path, file2_path) # end_time = time.time() # print(res, f"共耗时{end_time - start_time}") # 两个文件不一致,可能存在着被修改的风险,请您重新下载. 共耗时0.004996299743652344 print(res) # 两个文件不一致,可能存在着被修改的风险,请您重新下载.
网络文件校验(检查你下的文件和官方的是否一致(比如你在第三方网站下载(外网巨慢啊)的软件,跟官网的md5值作对比,保证速度与安全))
def check_file_md5(file_path, official_md5_str): import hashlib import os md = hashlib.md5() if os.path.exists(file_path): with open(file_path, 'rb') as file: # b模式不需要也不能指定 encoding for line in file: # 为了减少内存占用,分次读取 md.update(line) # md.update(file.read()) # 会占内存一点,但速度快了半秒啊(并且我也没感到占内存了) ---> 文件并无篡改痕迹,您可安心使用。 共耗时0.85650634765625 file_md = md.hexdigest() if file_md == official_md5_str: return "文件并无篡改痕迹,您可安心使用。" else: return "两个文件不一致,可能存在着被篡改的风险,请您重新下载。" else: return '您所传入的文件不存在,请检查是否有误。' file_path = r'F:\python听课笔记\测试\mysql-installer-community-8.0.16.0.msi' # 你要验证的文件 official_md5_str = 'c9cef27aea014ea3aeacabfd7496a092' # 官方提供给你的md5 值 # 这里我加上了检验用时统计 # import time # start_time = time.time() res = check_file_md5(file_path, official_md5_str) # 这里比较的是373MB的mysql安装包 # end_time = time.time() # print(res, f"共耗时{end_time - start_time}") print(res) # 文件并无篡改痕迹,您可安心使用。 共耗时1.4168531894683838
openpyxl模块
安装及版本支持
''' openpyxl 模块: 目前比较火的操作excel表格的模块 之前比较火的是 xlwd -- 写excel xlrt -- 读excel) 他们俩既支持03版本之前的excel文件,也支持03版本之后的excel文件 openpyxl 只支持03版本之后的 -- 只能支持后缀是 xlsx 的 03版本之前 excel文件的后缀名是 xls openpyxl 是第三方模块,要用就得先安装 '''
excel表格基础操作
写操作
from openpyxl import Workbook # 加载工作簿模块 wb = Workbook() # 先生成一个工作簿 wb1 = wb.create_sheet('index', 0) # 创建一个表单页 后面可以通过数字控制位置 wb2 = wb.create_sheet('index1') wb1.title = 'login' # 后期可以通过表单页对象点title修改表单页名称 wb1['A3'] = 666 # 在A 3 这个单元格添加数据 666 wb1['A4'] = 444 wb1.cell(row=6, column=3, value=88888888) # 在第六行第三列添加数字 88888888 wb1['A5'] = '=sum(A3:A4)' # 给A 5 这个单元格用求和函数 wb2['G6'] = 999 wb1.append(['username', 'age', 'hobby']) # 添加表头 wb1.append(['jason', 18, 'study']) # 表头下面添加记录 wb1.append(['tank', 72, '吃生蚝']) wb1.append(['egon', 84, '女教练']) wb1.append(['sean', 23, '会所']) wb1.append(['nick', 28, ]) # 不填的数据可以空着 wb1.append(['nick', '', '秃头']) # 保存新建的excel文件 wb.save('test.xlsx') # 后缀名必须是这个 xlsx
读操作
from openpyxl import load_workbook # 读文件 wb = load_workbook('test.xlsx', read_only=True, data_only=True) # data_only=True 就不会读出excel函数公式了 print(wb) print(wb.sheetnames) # ['login', 'Sheet', 'index1'] print(wb['login']['A3'].value) # 获取login表 的 A 3 单元格中的值 print(wb['login']['A4'].value) print(wb['login']['A5'].value) # None,通过代码产生的excel表格必须经过人为操作之后才能读取出函数计算出来的结果值 res = wb['login'] print(res) # 获取到的是一个区域,从A 0 为左上顶角 最右下含值单元格为右下顶角的区域打印出来 ge1 = res.rows for i in ge1: for j in i: print(j.value)
注意点:在执行写操作的时候要确保excel不处于被打开状态,否则程序会报错,无权限修改此内容
扩展链接:python openpyxl 常用功能
深拷贝与浅拷贝
用赋值的操作来将一个列表拷给另外一个列表
l = [1, 2, [1, 2]] l1 = l print(id(l), id(l1)) l[2].append(3) print(l, l1) # 改了l,而l1却也跟着变了,其内部用的其实是同一个列表,这是一个浅拷贝 # 2686864800328 2686864800328 # [1, 2, [1, 2, 3]] [1, 2, [1, 2, 3]]
那我想得到两个互相独立没有牵连又一模一样的列表咋整咧?
首先,这里要用 copy模块.....的两个方法
import copy # copy 浅拷贝 l = [1, 2, [1, 2]] l1 = copy.copy(l) # 拷贝一份 ....... 浅拷贝 l[2].append(33) print(l, l1) # 同样是浅拷贝 # [1, 2, [1, 2, 33]] [1, 2, [1, 2, 33]] # deepcopy 深拷贝 l = [1, 2, [1, 2]] l1 = copy.deepcopy(l) l[2].append(666) print(l, l1) # 内部引用的不是同一个列表了,深拷贝(不管你里面再套多少个列表,他引用的都不一样,两者都不会有任何的牵连) # [1, 2, [1, 2, 666]] [1, 2, [1, 2]] # 其原理是指向的东西,画张图就知道了
这里推荐一篇文章 Python中的赋值、浅拷贝、深拷贝
浅拷贝举例及原理分析图
深拷贝举例及原理分析图
额外:
confparse模块补充
type模块补充