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模組補充