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

包: # 包是一系列模块文件的结合体,表现形式是文件夹,该文件夹内部通常会包含一个__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"
dir/dir1/p/__init__.py
from dir.dir1 import p  # 导入包

print(p.hello())
# hello ya, i'm from dir/dir1/p
test2.py

上述过程发生了以下几件事情

"""
首次导入包:
    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")
dir/dir1/p/my_model.py
def hello():
    return "hello ya, i'm from dir/dir1/p"


from dir.dir1.p.my_model import *
dir/dir1/p/__init__.py
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
test2.py

此时,如果我觉得之前取的名字 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
运行后看多出来的 access.log 文件
'''
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参数会被忽略。
'''
basicConfig 的参数

发现上述案例运行后的日志文件中有乱码,且只出现在了文件里,似乎格式也不是我想要的,那么怎么解决呢?

首先,给你介绍几个对象

"""
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用户输出的消息
'''
formatter对象的参数

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)传递
        },
    },
}
conf/settings.py

在公共函数中写成公用方法

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
lib/common.py

在要用到的文件中引入使用(一般是在三层架构的接口层)

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)
interface/user_interface.py

生成日志文件案例

[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用户登录成功。]
log/ATM_Shop_Cart.log

小提示:项目正式上线后要把控制台的日志打印关掉,占资源(在配置字典片结尾处我有注释哦,在开发阶段可以利用控制台打印日志来调试)

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
mysql安装包为例的校验

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]]

# 其原理是指向的东西,画张图就知道了
copy模块浅拷贝深拷贝

这里推荐一篇文章 Python中的赋值、浅拷贝、深拷贝

浅拷贝举例及原理分析图

深拷贝举例及原理分析图

额外:

confparse模块补充

 

type模块补充

 

posted @ 2019-07-19 20:31  suwanbin  阅读(373)  评论(0编辑  收藏  举报