1. 程式人生 > 實用技巧 >Python基礎學習筆記(20)遞迴詳解 shutil 模組 logging 模組

Python基礎學習筆記(20)遞迴詳解 shutil 模組 logging 模組

Python基礎學習(20)遞迴詳解 shutil 模組 logging 模組

一、今日大綱

  • 遞迴詳解
  • shutil 模組
  • logging 模組

二、遞迴詳解

針對之前的斐波那契數列Fibonacci Sequence問題,經過不斷思考,運用遞迴、生成器、迴圈等方法,總結出了以下四種方法:

  • 遞迴方法1:

    # 遞迴方法1
    @log_interval
    def Fibonacci(n):
        def inner(args):
            if args == 1 or args == 0:
                return 1
            return inner(args - 1) + inner(args - 2)
    
        r = inner(n)
        return r
    
  • 遞迴方法2:

    # 遞迴方法2
    @log_interval
    def Fibonacci(n):
        def inner(args, a=1, b=1):
            if args == 0:
                return a
            a, b = b, a + b
            return inner(args - 1, a, b)
    
        return inner(n)
    
  • 迴圈方法:

    # 非遞迴方法
    def Fibonacci(n):
        a = 1
        b = 1
        for i in range(n):
            a, b = b, a + b
        return a
    
  • 生成器方法

    # 生成器方法
    def Fibonacci(n):
        i = 0
    
        def inner(args):
            a = 1
            yield a
            b = 1
            for i in range(args):
                a, b = b, a + b
                yield a
    
        for i in inner(n):
            pass
        return i
    

為了計算比較下列方法的時間複雜度,設計瞭如下裝飾器來比較四種方法的效率。

def read_dir(dirname):
    # if os.listdir(dirname) == []:
    #     return
    for i in os.listdir(dirname):
        if os.path.isfile(os.path.join(dirname, i)):
            print(i)
        if os.path.isdir(os.path.join(dirname, i)):
            read_dir(os.path.join(dirname, i))

嵌上裝飾器後,我們分別向四個函式傳入了不同的引數,下表是每個方法傳輸的不同引數以及所用時間:

傳入引數 返回時間
遞迴方法1 30 interval: 0.4075314998626709
遞迴方法2 50 interval: 0.0010001659393310547
迴圈方法 50000 interval: 0.044003963470458984
生成器方法 50000 interval: 0.04400515556335449

根據上表我們可知,效率方面生成器方法 ≈ 迴圈方法 > 遞迴方法2 >> 遞迴方法1

遞迴方法1:將一個問題通過分治法分解成兩個子問題,呼叫函式的次數也必將是最多的,所以其效率理所應當歸為最低;

遞迴方法2:將方法1返回的兩個子問題集合成了一個問題,大幅度地減少了函式的呼叫次數,效能產生了很大的提升,但是由於遞迴自身方法的限制,效率仍然不高;

迴圈方法:利用了和遞迴方法2同樣的思想,有點類似於連結串列頭插法儲存Pre-NodeNode的思想,不斷向下滾動生成所需的結果,由於只有一層迴圈,所以效率非常高。

生成器方法:利用剛剛學習的yield構建生成器的方法,也可以只需要一層迴圈實現斐波那契數列的計算,本質上也屬於迴圈方法,效率和上面的迴圈方法基本一致。

三、shutil 模組

shutil 模組對檔案和檔案集合提供了許多高階操作,特別是提供了支援檔案複製和刪除的函式。

  1. 拷貝檔案

    # `shutil.copy2(old_path, new_path)`
    shutil.copy2(r'D:\Python\Python Project\day20\lianjia.html',
                 r'D:\Python\Python Project\day21\lianjia.html')
    
  2. 拷貝目錄

    # shutil.copytree(old_path, new_path,
    #                 ignore=shutil.ignore_patterns('*.pyc'))
    # ignore=shutil.ignore_patterns('*.py') 表示不要所有的py檔案
    shutil.copytree(r'D:\Python\Python Project\day21\day09',
                    r'D:\Python\Python Project\day21\day09.bak',
                    ignore=shutil.ignore_patterns('*.py'))
    
  3. 刪除目錄(慎用)

    # shutil.rmtree(path, ignore_errors=True)  # ignore_errors=True 表示無視刪除的提示(比如檔案正在佔用)
    shutil.rmtree('day09', ignore_errors=True)
    
  4. 移動檔案/目錄

    # shutil.move(old_path, new_path, copy_function=shutil.copy2)
    
  5. 獲取磁碟使用空間

    total, used, free = shutil.disk_usage('c:\\')
    print('當前磁碟共: %iGB, 已使用: %iGB, 剩餘: %iGB'
          %(total / 1073741824, used / 1073741824, free/1073741824))
    # 1073741814 = 1024^3
    
    # %i是一種表十進位制的佔位符和%d基本沒有區別(找到了之後會寫在這裡)
    
  6. 壓縮檔案

    # shutil.make_archive(compressed_file_path, 'zip', file_path)
    shutil.make_archive('day09_z', 'zip', 'day09.bak')
    
  7. 解壓檔案

    # shutil.unpack_archive(compressed_file_path, 'zip', file_path)
    shutil.unpack_archive('day09_z.zip', 'day09_unz', 'zip')
    

四、logging 模組

  1. 日誌的意義

    • 用來排除錯誤
    • 用來做資料分析
  2. 日誌的意義——以網路購物商城為例

    一個專案需要構建一個數據庫存放足夠重要的內容;而還有一些相對來說沒那麼重要的內容,我們沒必要將它們放入資料庫,這時,本地構建日誌就成了我們的最佳選擇。

    如購物商城中:

    資料庫:什麼時間買了什麼商品、購物車中放置了哪些商品存入;

    本地日誌:使用者登入時間、搜尋記錄、使用者關閉時間、瀏覽商品清單等;

    比如我們擁有 10W 客戶,每天有 1W 條購買記錄需要存放在資料庫中,每天就會有10W+ 條和購買相關的資訊由於資料庫容量限制和網路傳輸限制,需要存放在本地日誌中;我們進行資料分析、操作審計、排查BUG等情況下,均需要檢查日誌記錄的情況,所以記錄日誌對於專案來說非常重要;而記錄日誌,就需要 logging 模組。

  3. 日誌資訊的等級

    由於需要記錄的資訊眾多,所以我們必須要對記錄的內容進行重要性排序,logging 模組為我們提供了以下五種重要性:

    # 輸出內容是有等級的,重要性從上至下依次升高:預設處理warning級別以上的所有資訊
    # logging.debug('debug message')
    # logging.info('info message')
    # logging.warning('warning message')
    # logging.error('error message')
    # logging.critical('critical message')
    

    logging 模組預設處理warning級別以上的所有資訊,而我們可以通過logging.basicConfig(level=logging.DEBUG)吧重要性調整為DEBUG等級,調整為其它等級同理。

  4. 日誌的設定

    • 輸出到螢幕

      logging.basicConfig(
          format='%(asctime)s - %(name)s - %(levelname)s[line : %(lineno)d] -%(module)s:  %(message)s',
          datefmt='%Y-%m-%d %H:%M:%S %p')
      
      logging.warning('warning test')  # 2020-07-18 21:22:00 PM - root - WARNING[line : 66] -03 logging 模組:  warning test
      
    • 輸出到檔案

      logging.basicConfig(
          format='%(asctime)s - %(name)s - %(levelname)s[line : %(lineno)d] -%(module)s:  %(message)s',
          datefmt='%Y-%m-%d %H:%M:%S %p',
          filename='tmp.log',
          level=logging.DEBUG,
          filemode='a'
      )
      
      logging.debug('debug test')
      logging.warning('warning test1')
      logging.warning('warning test2')
      
    • 同時向螢幕和檔案上輸出

      fh = logging.FileHandler('tmp.log', encoding='utf-8')  # 定義log的編碼型別和檔案路徑
      sh = logging.StreamHandler()
      logging.basicConfig(
          format='%(asctime)s - %(name)s - %(levelname)s[line : %(lineno)d] -%(module)s:  %(message)s',
          datefmt='%Y-%m-%d %H:%M:%S %p',
          level=logging.DEBUG,
          # handlers=[fh, sh]
      )
      
      logging.warning('warning test1')
      logging.warning('warning test2')
      logging.warning('warning test3')
      
    • 定時/定長記錄日誌

      import time
      from logging import handlers
      # 按照大小做切割,每個日誌1024位元組,每五個替換一次
      rh = handlers.RotatingFileHandler('myapp.log', maxBytes=1024, backupCount=5)
      # 按時間切,按秒切,只記錄距今6s的記錄
      th = handlers.TimedRotatingFileHandler(filename='x2.log', when='s', interval=6, encoding='utf-8')
      sh = logging.StreamHandler()
      logging.basicConfig(
          handlers=[rh, sh, th],
          format='%(asctime)s - %(name)s - %(levelname)s[line : %(lineno)d] -%(module)s:  %(message)s',
          datefmt="%Y-%m-%d %H:%M:%S %p",
          level=logging.DEBUG
      )
      
      for i in range(19999):
          time.sleep(0.5)
          logging.warning('warning test1')