python包-logging-hashlib-openpyxl模块-深浅拷贝-04

  • 2019 年 10 月 7 日
  • 筆記

包: # 包是一系列模块文件的结合体,表现形式是文件夹,该文件夹内部通常会包含一个__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模块补充