第八章 模塊&虛擬環(huán)境

模塊

一、模塊基礎(chǔ)

1、概述

  • 命名空間

    由于在Python中一切皆為對象(Object), 想要好好理解Module和Package,一定要先理解Namespace的概念。 所謂Namespace,是指標示符的可見范圍。對于Python而言,常見的Namespace主要有以下幾種

    • Build-in Namespace (內(nèi)建命名空間)
    • Global Namespace (全局命名空間)
    • Local Namespace (局部命名空間)

    有了命名空間的概念,可以有效的解決函數(shù)或者是變量重名的問題。不同的命名空間中允許出現(xiàn)相同的函數(shù)名或者 是變量名。它們彼此之間不會相互影響,例如在Namespace A和B中同時有一個名為var的變量,對A.var賦值并不 會改變B.var的值。

  • 為什么使用模塊?

    在計算機程序的開發(fā)過程中,隨著程序代碼越寫越多,在一個文件里代碼就會越來越長,越來越難以維護

  • 模塊概述

    為了編寫可維護性的代碼,我們會把很多函數(shù)進行分組,分別放到不同的文件里去。這樣,每個文件包含的代碼就相對較少,大多數(shù)編程語言都是采用這種方式來組織代碼的,在python中,一個.py文件就稱之為一個模塊

    Python中的一個Module對應(yīng)的就是一個.py文件。其中定義的所有函數(shù)或者是變量都屬于這個Module。這個Module 對于所有函數(shù)而言就相當于一個全局的命名空間。而每個函數(shù)又都有自己局部的命名空間。

  • 優(yōu)點

    • 提高了代碼的可維護性
    • 提高了代碼的復用度,編寫代碼不必從零開始,當一個模塊編寫完成,就可以在其他地方引用
    • 引用其他模塊,包含python內(nèi)置模塊和第三方模塊
    • 避免函數(shù)名和變量名等命名的沖突
  • 模塊分類

    • 標準庫模塊

    • 第三方模塊

    • 自定義模塊

2、使用標準庫中的模塊

  • time

    import time
    now = time.strftime("%Y-%m-%d %H:%M:%S")  # 獲取當前時間
    print(now)
    
  • random

    import random
    random_num = random.randrange(3)  # 返回012的隨機數(shù)
    print(random_num)
    

3、使用自定義模塊

  • 新建一個名為speak.py文件

    # speak.py 
    '''
    This is only a test module
    '''
    name = 'lucky'
    age = 18
    
    def speak():
      print("lucky is a very good man!")
    
  • import 語句

    • 作用

      導入模塊/包

    • 格式

      import module1[, module2[, module3[, ……]]]

      import module1 as 別名[, module2[, module3[, ……]]] 起別名

    • 注意

      一個模塊只會被導入一次,不管你執(zhí)行了多少次import,有效的防止導入模塊被一次次的執(zhí)行

    • 使用模塊中的內(nèi)容

      module.方法/類/變量

    不起別名實例

    import speak  # 導入speak模塊 
    >>> speak.name  # 獲取模塊speak中name的值
    lucky
    >>> speak.age  # 獲取模塊speak中age的值
    18
    >>> speak.speak()  # 調(diào)用模塊中speak方法
    lucky is a very good man!
    >>> print speak.__doc__  # 獲取模塊說明
    This is only a test module
    

    起別名實例

    >>> import speak as s  # 導入speak模塊 并起別名為s
    >>> s.name  # 獲取模塊speak中name的值
    lucky
    >>> s.age  # 獲取模塊speak中age的值
    18
    >>> s.speak()  # 調(diào)用模塊中speak方法
    lucky is a very good man!
    >>> print s.__doc__  # 獲取模塊說明
    This is only a test module
    
  • from ... import 語句

    • 作用

      從模塊中導入一些指定的部分

    • 格式

      from module import name1[, name2[, name3[, ……]]]

    實例

    >>> from speak import name,age,speak  # 從speak模塊導入 name,age,speak
    >>> name  # 獲取模塊speak中name的值
    lucky
    >>> age  # 獲取模塊speak中age的值
    18
    >>> speak()  # 調(diào)用模塊中speak方法
    lucky is a very good man!
    
  • from ... import * 語句

    • 概述

      將模塊中所有非下劃線開頭的成員都導入

    • 作用

      把一個模塊中所有的內(nèi)容全部導入當前命名空間

    • 格式

      from modulename import *

    • 注意

      不應(yīng)該過多使用,很可能造成變量名的沖突

    實例

    >>> from speak import *  # 會將speak模塊中非下劃線開頭的成員都導入當前命名空間中
    >>> name  # 獲取模塊speak中name的值
    lucky
    >>> age  # 獲取模塊speak中age的值
    18
    >>> speak()  # 調(diào)用模塊中speak方法
    lucky is a very good man!
    

4、__all__接口暴露

  • 概述

    代碼中是不提倡用 from xxx import * 的寫法的,但是在 console 調(diào)試的時候圖個方便還是很常見的。如果一個模塊 spam 沒有定義 __all__,執(zhí)行 from spam import * 的時候會將 spam 中非下劃線開頭的成員都導入當前命名空間中,這樣當然就有可能弄臟當前命名空間。如果顯式聲明了 __all__,import * 就只會導入 __all__ 列出的成員。如果 __all__ 定義有誤,列出的成員不存在,還會明確地拋出異常,而不是默默忽略。

  • 格式

    __all__ = ["name1", "name2"...]
    
  • 作用

    Python不像 Ruby 或者 Java,Python 沒有語言原生的可見性控制,而是靠一套需要大家自覺遵守的”約定“下工作。比如下劃線開頭的應(yīng)該對外部不可見。同樣,__all__ 也是對于模塊公開接口的一種約定,比起下劃線,__all__ 提供了暴露接口用的”白名單“。一些不以下劃線開頭的變量(比如從其他地方 import 到當前模塊的成員)可以同樣被排除出去。

  • 新建test_all.py

    # test_all.py
    '''
    This is only a test __all__ module
    '''
    
    __all__ = ["name", "speak"]  # 排除了 age
    
    name = 'lucky'
    age = 18
    
    def speak():
      print("lucky is a very good man!")
    

5、模塊循環(huán)引用

  • 概述

    出現(xiàn)循環(huán)引用其實就是模塊之間發(fā)生了相互依賴,A依賴B,B依賴A,這樣他們直接相互依賴,引用的時候就會出現(xiàn)者循環(huán)引用(交叉引用)

  • 現(xiàn)象

    有兩個模塊moduleA 和 moduleB

    moduleA.py

    from moduleB import b
    
    def a():
        print('我是A模塊的a方法')
        moduleB.b()
    def c():
        print('我是A模塊的c方法')
    
    if __name__ == '__main__':
        a()
    

    moduleB.py

    from moduleA import c
    
    def b():
      print('我是B模塊的b方法')
      c()
    
  • 導入的實質(zhì)

    導入其實是要將 被導入模塊所有的頂格代碼都執(zhí)行一遍,遇到函數(shù)和類的定義會作申明
    如果b模塊中有這么一句

    print('我是B模塊')
    

    你在a模塊impot b時就會 執(zhí)行 print('bbb')這一句

    回到循環(huán)引用中,首先導入B,進入B中,發(fā)現(xiàn)B中又導入了A又回到A中,但是A又導入B這就形成了循環(huán)引用

  • 解決方式1(直接導入模塊名,通過模塊調(diào)用其中的函數(shù))

    moduleA.py

    import moduleB
    
    def a():
        print('我是A模塊的a方法')
        moduleB.b()
    def c():
        print('我是A模塊的c方法')
    
    
    if __name__ == '__main__':
        a()
    

    moduleB.py

    import moduleA
    
    def b():
        print('我是B模塊的b方法')
        moduleA.c()
    
    
    if __name__ == '__main__':
        b()
    
  • 解決方式2(使用延遲導入(lazy import))

    內(nèi)部導入

    """
    moduleB.py
    """
    def b():
        from moduleA import c
        print('我是B模塊的b方法')
        c()
    

6、__name__屬性

  • 概述

    每個模塊都有一個name屬性,當其值為“main”時表明該模塊自身在運行,否則是被當做模塊導入,此時值為模塊的名字

  • 實例

    # speak.py 
    '''
    This is only a test module
    '''
    name = 'lucky'
    age = 18
    
    def speak():
      print("lucky is a very good man!")
      
    if __name__ == '__main__': 
       speak()
    
  • __name__作用

    模塊就是一個可執(zhí)行的python文件,一個模塊被另一個模塊導入,想讓模塊中的某一段代碼不執(zhí)行,可以使用__name__屬性來使程序隱藏該段代碼,當自身執(zhí)行時在執(zhí)行該塊代碼。一般作為判斷是否是作為主運行文件

  • 擴展

    以后主要用于程序入口使用(項目啟動文件中使用)

"""
Main.py
"""
def main():
    pass

if __name__ == '__main__':
    main()

7、包

  • 需求

    如果不同的人編寫的模塊名相同怎么辦?

  • 解決

    為了避免模塊名的沖突,python又引入了按目錄來組織模塊的方法,稱為包(package)

  • 特點

    引入包以后,只要頂層包名不與別人沖突, 那么所有的模塊都不會與別人沖突

  • 注意

    每個包目錄下都會有一個名為__init__.py的文件,說明這個目錄是個python包,還可以導出包中的內(nèi)容

  • 建包

    新建文件夾名稱為lucky_package文件夾

    目錄結(jié)構(gòu)

    project/
      lucky_package/
          __init__.py  # 聲明lucky_package為一個包
        speak.py  # 模塊speak
      test.py  # 用于測試lucky_package包的使用
    

    實現(xiàn)

    • 方式一 通過pycharm直接創(chuàng)建Python包

      選擇模塊 -> New -> Python Package

屏幕快照 2019-12-25 下午2.52.15.png
輸入包名
屏幕快照 2019-12-25 下午2.52.44.png
點擊OK
屏幕快照 2019-12-25 下午2.52.54.png
  • 方式二 手動創(chuàng)建

    • 模塊 -> New -> Directory
屏幕快照 2019-12-25 下午2.56.17.png
- 輸入 lucky_package

![屏幕快照 2019-12-25 下午2.52.44-7256895.png](https://upload-images.jianshu.io/upload_images/11222021-d7744dfe5c180b83.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)


- 點擊OK

  ![屏幕快照 2019-12-25 下午2.56.48.png](https://upload-images.jianshu.io/upload_images/11222021-354042fa73daaf93.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)


  

- lucky_package -> New -> Python File
屏幕快照 2019-12-25 下午2.56.52.png
- 輸入文件名稱為 `__init__.py`
屏幕快照 2019-12-25 下午2.57.00.png
  - 點擊 OK

  ![屏幕快照 2019-12-25 下午2.57.00.png](https://upload-images.jianshu.io/upload_images/11222021-aa49d984a7fc1c40.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
  • 包內(nèi)創(chuàng)建模塊

    speak.py

    # speak.py 
    '''
    This is only a test module
    '''
    name = 'lucky'
    age = 18
    
    def speak():
      print("lucky is a very good man!")
      
    if __name__ == '__main__': 
       speak()
    
  • 使用

    test.py

    • 第一種導入方式使用

      >>> from lucky_package import speak  # 從lucky_package包導入speak模塊
      >>> speak.name  # 獲取模塊speak中name的值
      lucky
      >>> speak.age  # 獲取模塊speak中age的值
      18
      >>> speak.speak()  # 調(diào)用模塊中speak方法
      lucky is a very good man!
      

      導包重命名

      >>> from lucky_package import speak as s  # 從lucky_package包導入speak模塊并重命名為s
      >>> s.name  # 獲取模塊speak中name的值
      lucky
      
    • 第二種導入方式

      >>> from lucky_package.speak import name,age,speak  # 從lucky_package包speak模塊導入 name,age,speak
      >>> name  # 獲取模塊speak中name的值
      lucky
      >>> age  # 獲取模塊speak中age的值
      18
      
    • 第三種導入方式

      >>> from lucky_package.speak import *  # 從lucky_package包speak模塊導入 所有成員
      >>> name  # 獲取模塊speak中name的值
      lucky
      >>> age  # 獲取模塊speak中age的值
      18
      
    • 第四種導入方式

      >>> import lucky_package.speak  # 導入lucky_package里speak包
      >>> lucky_package.speak.name  # 獲取name的值
      lucky
      
    • 第五種導入方式

      >>> import lucky_package.speak as s  # 導入lucky_package里speak包
      >>> s.name  # 獲取name的值
      lucky
      
  • 填充包__init__.py代碼

    • 方式一 在init.py中 導入模塊

      __init__.py

      from . import speak
      

      使用

      >>> from lucky_package import speak   # 導入lucky_package里speak包
      >>> speak.name  # 獲取name的值
      lucky
      
    • 方式二

      在init.py中 導入模塊所有成員

      __init__.py

      from .speak import * 
      

      使用

      >>> from lucky_package import *   # 導入lucky_package里speak包
      >>> name  # 獲取name的值
      lucky
      
    • 注意

      不建議這樣使用方式

二、常用模塊

1、time模塊

  • 名詞
    UTC(世界協(xié)調(diào)時間):格林尼治時間,世界標準時間,北京時間1970年01月01日08時00分00秒)起至現(xiàn)在的總秒數(shù),在中國為UTC+8

    DST(夏令時):是一種為了節(jié)約能源而人為規(guī)定地方時間的制度,一般在天亮早的夏季將時間提前一小時

  • 時間的表示形式

    • 時間戳
      以整型或浮點型表示的是一個以秒為單位的時間間隔,這個時間的基礎(chǔ)值是1970年1月1號零時開始算

    • 元組
      一種python的數(shù)據(jù)結(jié)構(gòu)表示方式,這個元組有9個整數(shù)元素,分別表示不同的時間含義
      year 年
      month(1-12) 月
      day(1-31) 日
      hours(0-23) 時
      minutes(0-59) 分
      seconds(0-59) 秒
      weekday(0-6,0表示星期一) 周
      Julian day(1-366):表示當前日期在本年度是第幾天
      DST flag(-1 or 0 or 1):夏令時格式,0表示正常格式,1表示夏令時格式,-1表示根據(jù)當前的時間格式來判定

    • 導入

import time
  • 格式化日期的函數(shù)

    | 函數(shù) | 函數(shù)說明 |
    | :-------------------------------------------------------- | ------------------------------------------------------------ |
    | time.time() | 當前時間戳(秒數(shù)) UNIX和Windows只支持到2038年。 |
    | time.localtime() | 接收時間輟(1970紀元后經(jīng)過的浮點秒數(shù))并返回當?shù)貢r間下的時間元組 (0是周一) |
    | time.strftime("%Y-%m-%d %H:%M:%S") | 函數(shù)接收以時間元組,并返回以可讀字符串表示的當?shù)貢r間,格式由參數(shù) format 決定。第二個參數(shù)可有可無 |
    | time.asctime() | 返回格式化后的英文文本的時間 |
    | time.mktime(tupletime) | 接受時間元組并返回時間輟 |
    | time.sleep(secs) | 推遲調(diào)用線程的運行,secs指秒數(shù)。 |
    | time.clock() | 用以浮點數(shù)計算的秒數(shù)返回當前的CPU時間。用來衡量不同程序的耗時,比time.time()更有用。 |
    | time.strptime("2013-10-10 23:40:00", "%Y-%m-%d %H:%M:%S") | 將給定時間轉(zhuǎn)換為時間元組 |

  • 時間日期格式化符號

    符號 說明 符號 說明
    %Y 4位的年 %y 2位的年
    %m 月份(01-12) %d 月內(nèi)中的一天(0-31)
    %H 24小時制小時數(shù)(0-23) %I 12小時制小時數(shù)(01-12)
    %M 分鐘數(shù)(00=59) %S 秒(00-59)
    %a 本地簡化星期名稱 英文文本 簡化 %A c本地簡化星期名稱 英文文本 完整
    %j 年內(nèi)的一天(001-366) %w 星期(0-6),星期天為星期的開始 (0是周一)
    %x 本地相應(yīng)的日期表示 (08/02/17) %X 本地相應(yīng)的時間表示 23:48:34
  • time()

    返回當前時間的時間戳,無需參數(shù),返回值為浮點型

    t1 = time.time()
    print(t1)
    
  • localtime([t])

    將給定的時間戳轉(zhuǎn)為本地時間元組格式,如果沒有參數(shù)默認轉(zhuǎn)換當前時間戳

    t3 = time.localtime()
    print(t3)
    
  • mktime(tt)

    將本地時間元組轉(zhuǎn)為時間戳

    t4 = time.mktime(t3)
    print(t4)
    
    import time
    
    t = (2016, 2, 17, 17, 3, 38, 1, 48, 0)
    secs = time.mktime( t )
    print ("time.mktime(t) : %f" %  secs)#time.mktime(t) : 1455699818.000000
    time.strptime(“2013-10-10 23:40:00”, "%Y-%m-%d %H:%M:%S")
    time = time.mktime(time.strptime("2013-10-10 23:40:00", "%Y-%m-%d %H:%M:%S"))
    print(time.strftime("%Y-%m-%d %H:%M:%S",time.localtime(time)))
    
  • asctime([tt])

    將時間元組格式轉(zhuǎn)為指定格式的字符串形式,如果沒有參數(shù)默認使用localtime時間的時間元組

    t5 = time.asctime(t3)
    print(t5, type(t5))
    
  • strftime(format[, tt])

    將時間元組以指定的格式轉(zhuǎn)為字符串,如果沒有tt參數(shù)默認使用當前本地時間元組

      t7 = time.strftime("%Y-%m-%d %H:%M:%S", t3)
      print(t7)
    
  • strptime(st, format)

    將指定格式的字符串解析為時間元組,是strftime的逆過程

    注意:format表示的格式要與st一致

    t8 = time.strptime("2001-10-01 08:08:08", "%Y-%m-%d %H:%M:%S")
    print(t8)
    
  • sleep()

    延遲一個時間段,接收整型或者浮點型

    time.sleep(2)
    
  • clock()

    返回當前程序執(zhí)行時間,Unix系統(tǒng)始終返回全部運行時間,而Windows系統(tǒng)從第二次開始都是以第一次調(diào)用此函數(shù)的時間戳為基準,而不是以程序開始的時間為基準

    #                   windows   Unix
    print(time.clock()) # 0         1
    time.sleep(1)
    print(time.clock()) # 1         2
    time.sleep(1)
    print(time.clock()) # 2         3
    
  • 案例

    計算程序運行時間

    import time
    
    def procedure():
        for i in range(10000):
           pass
    
    # time.clock
    t0 = time.clock()
    procedure()
    print (time.clock() - t0)
    
    # time.time
    t0 = time.time()
    procedure()
    print (time.time() - t0)
    

    計算一個人活了多久

    import time
    # 將其轉(zhuǎn)換為時間元組
    time_tup = time.strptime("1993-10-10 23:40:00", "%Y-%m-%d %H:%M:%S")
    before_time = time.mktime(time_tup)  # 根據(jù)實際元組返回秒數(shù)
    now_time = time.time() - before_time
    print(now_time/60/60,'小時')
    print(now_time/60/60/24,'天')
    print(now_time/60/60/24/365,'年')
    print(round(now_time/60/60/24/365, 2),'年')
    

2、datetime模塊

  • 概述

datetime比time高級,可以理解為datetime基于time進行了封裝,提供了更簡單的函數(shù)接口,datetime模塊的接口更直觀、更容易調(diào)用

  • 模塊中的類

    • time 只關(guān)注時間
    • date 只關(guān)注日期
    • datetime 同時關(guān)注日期和時間
    • timedelta 主要用于計算時間跨度
    • tzinfo 時區(qū)相關(guān)
  • 導入

    import datetime
    
  • 方法

    • 獲取當前時間

      t1 = datetime.datetime.now()
      print(t1, type(t1))
      
    • 獲取指定日期時間

      t2 = datetime.datetime(2001, 10, 1, 8, 8, 8)
      print(t2)
      
    • 將datetime時間轉(zhuǎn)對象為字符串

      t3 = t1.strftime("%X %x")
      print(t3, type(t3))
      
    • 將字符串轉(zhuǎn)為datetime對象

      t4 = datetime.datetime.strptime(t3, "%X %x")
      print(t4, type(t4))
      
    • 時間相減,返回一個時間間隔

      t5 = datetime.datetime(2001, 10, 1, 8, 8, 8)
      t6 = datetime.datetime(2001, 10, 2, 9, 8, 9)
      t7 = t6- t5
      print(t7, type(t7))
      #獲取時間間隔天數(shù)
      print(t7.days)
      #獲取去除間隔天數(shù)以外的間隔秒數(shù)
      print(t7.seconds)
      
    • Python 獲取昨天日期

      # 引入 datetime 模塊
      import datetime
      def getYesterday(): 
          today=datetime.date.today() 
          oneday=datetime.timedelta(days=1) 
          yesterday=today-oneday  
          return yesterday
       
      # 輸出
      print(getYesterday())
      

3、calendar模塊

  • 概述

    日歷模塊

  • 導入

    import calendar
    
  • 方法

    • 返回指定年的某月

      print(calendar.month(2019, 2))
      
    • 返回指定年的日歷

    print(calendar.calendar(2018))
    
    • 判斷是否是閏年

      print(calendar.isleap(2000))
      
    • 返回某個月的weekday的第一天和這個月的所有天數(shù)

      print(calendar.monthrange(2019, 4))
      
    • 返回某一個月以一周為周期的元素序列

      print(calendar.monthcalendar(2019, 2))
      

4、Pillow模塊

  • 概述

    PIL:Python Imaging Library,已經(jīng)是Python平臺事實上的圖像處理標準庫了。PIL功能非常強大,但API卻非常簡單易用。

    由于PIL僅支持到Python 2.7,加上年久失修,于是一群志愿者在PIL的基礎(chǔ)上創(chuàng)建了兼容的版本,名字叫Pillow,支持最新Python 3.x,又加入了許多新特性,因此,我們可以直接安裝使用Pillow。

  • 安裝Pillow

    如果安裝了Anaconda,Pillow就已經(jīng)可用了。否則,需要在命令行下通過pip安裝:

    $ pip install pillow
    

    如果遇到Permission denied安裝失敗,請加上sudo重試。

    • 操作圖像

    來看看最常見的圖像縮放操作,只需三四行代碼:

    from PIL import Image
    
    # 打開一個jpg圖像文件,注意是當前路徑:
    im = Image.open('test.jpg')
    # 獲得圖像尺寸:
    w, h = im.size
    print('Original image size: %sx%s' % (w, h))
    # 縮放到50%:
    im.thumbnail((w//2, h//2))
    print('Resize image to: %sx%s' % (w//2, h//2))
    # 把縮放后的圖像用jpeg格式保存:
    im.save('thumbnail.jpg', 'jpeg')
    

    其他功能如切片、旋轉(zhuǎn)、濾鏡、輸出文字、調(diào)色板等一應(yīng)俱全。

    比如,模糊效果也只需幾行代碼:

    from PIL import Image, ImageFilter
    
    # 打開一個jpg圖像文件,注意是當前路徑:
    im = Image.open('test.jpg')
    # 應(yīng)用模糊濾鏡:
    im2 = im.filter(ImageFilter.BLUR)
    im2.save('blur.jpg', 'jpeg')
    

    驗證碼實現(xiàn)

    from PIL import Image, ImageDraw, ImageFont, ImageFilter
    
    import random
    
    # 隨機字母:
    def rndChar():
        return chr(random.randint(65, 90))
    
    # 隨機顏色1:
    def rndColor():
        return (random.randint(64, 255), random.randint(64, 255), random.randint(64, 255))
    
    # 隨機顏色2:
    def rndColor2():
        return (random.randint(32, 127), random.randint(32, 127), random.randint(32, 127))
    
    # 240 x 60:
    width = 60 * 4
    height = 60
    image = Image.new('RGB', (width, height), (255, 255, 255))
    # 創(chuàng)建Font對象:
    # windwos下
    font = ImageFont.truetype(r'C:\Windows\Fonts/Arial.ttf', 36)
    # Mac下
    # font = ImageFont.truetype('Arial.ttf', 36)
    # 創(chuàng)建Draw對象:
    draw = ImageDraw.Draw(image)
    # 填充每個像素:
    for x in range(width):
        for y in range(height):
            draw.point((x, y), fill=rndColor())
    # 輸出文字:
    for t in range(4):
        draw.text((60 * t + 10, 10), rndChar(), font=font, fill=rndColor2())
    # 模糊:
    image = image.filter(ImageFilter.BLUR)
    image.save('code.jpg', 'jpeg')
    

    作業(yè):自己寫一個實現(xiàn)等比縮放的函數(shù)

    4、uuid模塊

  • 概述

    是128位的全局唯一標識符,通常由32字節(jié)的字母串表示,它可以保證時間和空間的唯一性,也稱為GUID

  • 作用

    隨機生成字符串,在token、賬號、訂單號等需要唯一標識的地方使用

  • 原理

    通過Mac地址、時間戳、命名空間、隨機數(shù)、偽隨機數(shù)來保證產(chǎn)生的id的唯一性

  • 算法:

    • uuid1()基于時間戳
      有MAC地址、當前時間戳、隨機數(shù)字,可以保證全球范圍內(nèi)的唯一性。但是由于MAC地址的使用會帶來安全問題,局域網(wǎng)中可以使用IP來代替MAC

    • uuid2()基于分布式計算環(huán)境DCE
      算法和uuid1()相同,不同的是把時間戳的前4位換成POSI的UID,實際當中很少使用

      注意:python中沒有這個函數(shù)

    • uuid3()基于名字和MD5散列值
      通過計算名和命名空間的MD5散列值得到,保證了同一命名空間中不同名字的唯一性,和不同命名空間的唯一性,但是同一命名空間的相同名字生成相同的uuid

    • uuid4()基于隨機數(shù)
      由偽隨機數(shù)得到的,有一定的重復概率,這個重復概率是可以通過計算得到的

    • uuid5()基于名字和SHA1散列值
      算法和uuid3()相同,不同的是使用SHA1算法

  • 使用經(jīng)驗:

    • python中沒有基于DCE的,所以uuid2()可以忽略
    • uuid4()存儲概率性重復,由于無映射性所以最好不用
    • 如果在全局的分布式環(huán)境下,最好使用uuid1()
    • 若名字的唯一性要求,最好使用uuid3()或者uuid5()
  • 導入

    import uuid
    
  • 使用

    a = uuid.uuid1()
    print(a, type(a))
    b = str(a)
    print(b, type(b))
    # print(uuid.uuid2())
    print(uuid.uuid3(uuid.NAMESPACE_DNS, "lucky"))
    print(uuid.uuid3(uuid.NAMESPACE_DNS, "lucky"))
    print(uuid.uuid3(uuid.NAMESPACE_OID, "lucky"))
    print(uuid.uuid3(uuid.NAMESPACE_DNS, "lucky"))
    print(uuid.uuid5(uuid.NAMESPACE_DNS, "lucky"))
    

5、collections模塊

  • 概述

    python中內(nèi)建的一個集合模塊,提供了許多有用的集合類

  • namedtuple

    • 概述

      命名元組,本質(zhì)是一個函數(shù),用他來創(chuàng)建一個自定義的tuple類型

      規(guī)定tuple元素的格式,并可以用屬性而不是索引引用tuple中的元素

      用namedtuple實際上是創(chuàng)建一個新的數(shù)據(jù)類型

    • 導入

      from collections import namedtuple
      
    • 使用

      #假設(shè)這是一個點的坐標,但是沒有寫注釋,時間久了就忘了該有含義
      p = (1, 2)
      
      
      # 定義一個新的數(shù)據(jù)類型,
      Point = namedtuple("point", ["x", "y"])
      # 定義一個Point類型的變量,保存一個元組數(shù)據(jù)
      p2 = Point(1, 2)
      print(p2, isinstance(p2, Point), isinstance(p2, tuple))
      #訪問命名元組元素的值
      print(p2[0], p2[1])
      print(p2.x, p2.y)
      
  • deque

    • 概述

      使用list存儲數(shù)據(jù),按索引訪問元素,但是插入和刪除元素會根據(jù)元素的個數(shù)增多個降低效率。因為list是線性存儲,數(shù)據(jù)量大插入和刪除的效率就會低。

      deque就是為了高效實現(xiàn)插入和刪除操作的雙向列表,適用于隊列和棧。并且deque是線程安全的

    • 導入

      from collections import deque
      
    • 使用

      q = deque([1,2,3,4,5])
      
      q.append(6)
      q.appendleft(0)
      
      print(q.pop())
      print(q.popleft())
      
      print(q)
      
  • defaultdict

    • 概述

      使用dict時,如果引用的key不存在,如果使用的[]方式則會報KeyError異常,如果使用的get()方式則會得到None。
      如果希望key不存在時也能得到一個默認的值就使用defaultdict

    • 導入

      from collections import defaultdict
      
    • 使用

      d1 = {"a": 1, "b": 2, "c": 3}
      # print(d1["d"])
      # print(d1.get("d"))
      
      
      d2 = defaultdict(lambda :"鍵值對不存在")
      d2["a"] = 1
      d2["b"] = 2
      print(d2["c"])
      print(d2.get("c"))
      print(d2, type(d2), isinstance(d2, dict))
      
  • OrderedDict

    • 概述

      使用dict是,key是無序的。對dict做迭代時無法保證key的順序。如果需要key有順序,就可以使用OrderDict

    • 導入

      from collections import OrderedDict
      
    • 使用

      d1 = {"a": 1, "b": 2, "c": 3}
      for key in d1:
          print(key)
      print("----------------------")
      
      
      
      d2 = OrderedDict([("a", 1),("b", 2),("c", 3)])
      print(d2)
      print(d2["a"])
      print(d2.get("b"))
      
  • Counter

    • 概述

      是一個簡單的計數(shù)器,本質(zhì)上是dict的一個子類

    • 導入

      from collections import Counter
      
    • 使用

      需求:計算集合中每個字符出現(xiàn)的次數(shù)

      s = "lucky is a good man"
      c = Counter()
      print(c, type(c), isinstance(c, dict))
      for ch in s:
          c[ch] = c[ch] + 1
      print(c)
      for key in c:
          print(key, c[key])
      

6、base64模塊

  • 概述

    用記事本打開圖片等文件,看到一坨亂碼,因為二進制文件包含很多無法顯示的內(nèi)容。所以想讓記事本能處理二進制數(shù)據(jù),就需要將二進制字符串轉(zhuǎn)換,base64是一種比較常見的二進制編碼方式

  • 作用

    適用于小段內(nèi)容的編碼,比如數(shù)字證書簽名、cookie、網(wǎng)頁中傳輸?shù)纳倭慷M制數(shù)據(jù)

  • 編碼原理

    一個包含64個字符的列表
    ["A", "B", ……, "Z", "a", "b", ……, "z", "0", "1", ……, "9", "+", "/"]
    對二進制數(shù)據(jù)進行處理,每是三個字節(jié)一組,一組就是3x8=24bit,劃分為4組,每組正好6bit。得到4個數(shù)字作為索引,然后查表,獲取相應(yīng)的4個字符,就是編碼后的字符串

  • 注意

    base64是一種通過查表的編碼方法,不能用于加密,即使修改了字符對照表也不行

  • 使用

    • b64encode

      s1 = b"lucky is a good man"
      print(base64.b64encode(s1))
      s2 = b'c3VuY2sgaXMgYSBnb29kIG1hbg=='
      print(base64.b64decode(s2))
      

      由于=字符也可能出現(xiàn)在base64編碼中,但是=在url、cookie里會造成歧義,所以很多base64編碼會把編碼后的=去掉

      s6 = b"abcd"
      s7 = base64.b64encode(s6)
      print(s7)
      '''
      'YWJjZA=='
      'YWJjZA'
      '''
      s8 = b'YWJjZA=='
      print(base64.b64decode(s8))
      

      注意

      由于標準base64編碼后可能出現(xiàn)字符+或/,在URL中就不能直接作為參數(shù)

    • urlsafe_b64encode

      提供urlsafe_b64encode編碼,保證url的安全,將+和/替換為-和_,提供urlsafe_b64decode進行url安全解碼

      s3 = b"http://www.xialigang.com"
      print(base64.urlsafe_b64encode(s3))
      s4 = b"aHR0cDovL3d3dy5zdW5jay53YW5n"
      print(base64.urlsafe_b64decode(s4))
      
      s5 = b"lucky is a good m~"
      print(base64.b64encode(s5))
      print(base64.urlsafe_b64encode(s5))
      

7、hashlib模塊

  • 概述

    該模塊提供了常見的摘要算法,如MD5、SHA1

    摘要算法(又稱哈希算法、散列算法):它通過一個函數(shù),把任意長度的數(shù)據(jù)轉(zhuǎn)為一個長度固定的數(shù)據(jù)串(通常用16進制的字符串表示)

  • 作用

    用于加密

  • MD5
    最常見的摘要算法,速度快,生成的結(jié)構(gòu)是128位字節(jié),通常用32位16進制字符串表示

  • 使用

    s1 = b"lucky is a good man"
    m1 = hashlib.md5()
    m1.update(s1)
    ret = m1.hexdigest()
    print(ret)
    
    #如果數(shù)據(jù)量比加大,可以分多次調(diào)用update,最終結(jié)果是一樣的
    m2 = hashlib.md5()
    m2.update(b"lucky is a")
    m2.update(b" good man")
    ret2 = m2.hexdigest()
    print(ret2)
    
  • SHA1
    調(diào)用SHA1與調(diào)用MD5完全一樣,SHA1的結(jié)果是160字節(jié),通常用40位16進制字符串表示

  • 使用

    s2 = b"lucky is a good man"
    sh1 = hashlib.sha1()
    sh1.update(s2)
    ret3 = sh1.hexdigest()
    print(ret3)
    

    注意:數(shù)據(jù)量大同md5使用相同

  • 更安全的
    SHA256
    SHA512
    越安全的算法不見越慢,而且摘要越長

  • 應(yīng)用:
    任何允許用戶登錄的網(wǎng)站都會存儲用戶登錄的用戶名和密碼(存儲在數(shù)據(jù)庫中),那么密碼一般存儲的是原密碼的摘要值
    lucky-666666明文存儲到數(shù)據(jù)庫中,如果數(shù)據(jù)庫泄露,所有用戶信息就會暴露

    正確的保存口令方式不是存儲明文,而是存儲口令的摘要,當用戶登錄時,首先計算用戶輸入的明文的摘要,和數(shù)據(jù)庫中的對比,如果一致說明口令正確,否則一定錯誤

8、hmac模塊

  • 概述

    實現(xiàn)了HMAC算法,是用一個key對數(shù)據(jù)進行“雜湊”后在進行的hash,是用hmac比hash算法更安全,不同的key會產(chǎn)生不同的hash值

  • 導入

    s = b"lucky is a good man"
    key = b"good"
    h = hmac.new(key, s, digestmod="MD5")
    ret = h.hexdigest()
    print(ret)
    

三、第三方模塊

1、安裝

  • Windows
    pip install 模塊名
    pip install 模塊名==版本號

  • linux(root用戶)
    pip install 模塊名
    pip install 模塊名==版本號

  • linux(普通用戶)
    sudo pip install 模塊名
    sudo pip install 模塊名==版本號

  • 注意

    公司里基本你不會讓你用root用戶

2、卸載

  • Windows
    pip uninstall 模塊名
  • linux(root用戶)
    pip uninstall 模塊名
  • linux(普通用戶)
    sudo pip uninstall 模塊名

3、查看

  • 當前所有的三方模塊
    pip list
  • pip的版本
    pip --version

4、實際操作

  • pillow

    處理圖像

  • 安裝

    pip install pillow

  • 實例

    from PIL import Image
    
    #打開圖片
    im = Image.open("timg.jpg")
    # 查看圖片信息
    print(im.format, im.size, im.mode)
    #修改圖片大小
    im.thumbnail((102, 68))
    #生成新圖片
    im.save("timg2.jpg", "JPEG")
    

四、virtualenv虛擬環(huán)境

1、概述

在開發(fā)Python應(yīng)用程序的時候,系統(tǒng)安裝的Python3只有一個版本:3.5。所有第三方的包都會被pip安裝到Python3的site-packages目錄下。

如果我們要同時開發(fā)多個應(yīng)用程序,那這些應(yīng)用程序都會共用一個Python,就是安裝在系統(tǒng)的Python 3。如果應(yīng)用A需要jinja 2.7,而應(yīng)用B需要jinja 2.6怎么辦?

這種情況下,每個應(yīng)用可能需要各自擁有一套“獨立”的Python運行環(huán)境。virtualenv就是用來為一個應(yīng)用創(chuàng)建一套“隔離”的Python運行環(huán)境。

2、安裝

我們用pip安裝virtualenv:

$ pip3 install virtualenv

然后,假定我們要開發(fā)一個新的項目,需要一套獨立的Python運行環(huán)境,可以這么做:

3、操作

  • 第一步,創(chuàng)建目錄:
Mac:~ xialigang$ mkdir myproject
Mac:~ xialigang$ cd myproject/
Mac:myproject xialigang$
  • 第二步,創(chuàng)建一個獨立的Python運行環(huán)境,命名為venv
Mac:myproject xialigang$ virtualenv --no-site-packages venv
Using base prefix '/usr/local/.../Python.framework/Versions/3.4'
New python executable in venv/bin/python3.4
Also creating executable in venv/bin/python
Installing setuptools, pip, wheel...done.

命令virtualenv就可以創(chuàng)建一個獨立的Python運行環(huán)境,我們還加上了參數(shù)--no-site-packages,這樣,已經(jīng)安裝到系統(tǒng)Python環(huán)境中的所有第三方包都不會復制過來,這樣,我們就得到了一個不帶任何第三方包的“干凈”的Python運行環(huán)境。

新建的Python環(huán)境被放到當前目錄下的venv目錄。有了venv這個Python環(huán)境,可以用source進入該環(huán)境:

Mac:myproject xialigang$ source venv/bin/activate
(venv)Mac:myproject xialigang$

注意到命令提示符變了,有個(venv)前綴,表示當前環(huán)境是一個名為venv的Python環(huán)境。

  • 安裝各種第三方包,并運行python命令:
(venv)Mac:myproject xialigang$ pip install jinja2
...
Successfully installed jinja2-2.7.3 markupsafe-0.23
(venv)Mac:myproject xialigang$ python myapp.py
...

venv環(huán)境下,用pip安裝的包都被安裝到venv這個環(huán)境下,系統(tǒng)Python環(huán)境不受任何影響。也就是說,venv環(huán)境是專門針對myproject這個應(yīng)用創(chuàng)建的。

  • 退出當前的venv環(huán)境,使用deactivate命令:
(venv)Mac:myproject xialigang$ deactivate 
Mac:myproject xialigang$

此時就回到了正常的環(huán)境,現(xiàn)在pippython均是在系統(tǒng)Python環(huán)境下執(zhí)行。

完全可以針對每個應(yīng)用創(chuàng)建獨立的Python運行環(huán)境,這樣就可以對每個應(yīng)用的Python環(huán)境進行隔離。

virtualenv是如何創(chuàng)建“獨立”的Python運行環(huán)境的呢?原理很簡單,就是把系統(tǒng)Python復制一份到virtualenv的環(huán)境,用命令source venv/bin/activate進入一個virtualenv環(huán)境時,virtualenv會修改相關(guān)環(huán)境變量,讓命令pythonpip均指向當前的virtualenv環(huán)境。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容