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