天天看點

python基礎 -23- 子產品(random,chardet,os,sys,copy,time,datetime,pytz,pickle,json,MD5,SHA-1,shutil,re等)

為什麼是子產品?

 在計算機程式的開發過程中,随着程式代碼越寫越多,在一個檔案裡代碼就會越來越長,越來越不容易維護。

 為了編寫可維護的代碼,我們把很多函數分組,分别放到不同的檔案裡,這樣,每個檔案包含的代碼就相對較少,很多程式設計語言都采用這種組織代碼的方式。在Python中,一個.py檔案就可以稱之為一個子產品(Module)。

使用子產品有什麼好處?

  1. 最大的好處是大大提高了代碼的可維護性。其次,編寫代碼不必從零開始。當一個子產品編寫完畢,就可以被其他地方引用。我們在編寫程式的時候,也經常引用其他子產品,包括Python内置的子產品和來自第三方的子產品。
  2. 使用子產品還可以避免函數名和變量名沖突。每個子產品有獨立的命名空間,是以相同名字的函數和變量完全可以分别存在不同的子產品中,是以,我們自己在編寫子產品時,不必考慮名字會與其他子產品沖突

子產品分類

子產品分為三種:

  • 内置标準子產品(又稱标準庫)執行help(‘modules’)檢視所有python自帶子產品清單
  • 第三方開源子產品,可通過pip install 子產品名 聯網安裝
  • 自定義子產品

子產品導入&調用

import module_a  #導入
from module import xx
from module.xx.xx import xx as rename #導入後重指令
from module.xx.xx import *  #導入一個子產品下的所有方法,不建議使用
__import__("test_name")  # 動态導入
__import__("basicGrammar.module.my_module")  # 動态導入(跨包)
importlib.import_module("test_name")  # 動态導入
importlib.import_module("basicGrammar.module.my_module")  # 動态導入(跨包)

module_a.xxx  #調用
           
注意:子產品一旦被調用,即相當于執行了另外一個py檔案裡的代碼

自定義子產品

 這個最簡單, 建立一個.py檔案,就可以稱之為子產品,就可以在另外一個程式裡導入

python基礎 -23- 子產品(random,chardet,os,sys,copy,time,datetime,pytz,pickle,json,MD5,SHA-1,shutil,re等)

子產品查找路徑(跨目錄導入)

 發現,自己寫的子產品隻能在目前路徑下的程式裡才能導入,換一個目錄再導入自己的子產品就報錯說找不到了, 這是為什麼?

 這與導入子產品的查找路徑有關

import sys
print(sys.path)
           

 輸出(注意不同的電腦可能輸出的不太一樣)

['D:\\workspace_python\\basis_learn01\\basicGrammar\\module', 
 'D:\\workspace_python\\basis_learn01', 
 'E:\\PyCharm\\PyCharm 2020.1.3\\plugins\\python\\helpers\\pycharm_display',  'E:\\PyCharm\\Python38\\python38.zip', 
 'E:\\PyCharm\\Python38\\DLLs', 
 'E:\\PyCharm\\Python38\\lib', 
 'E:\\PyCharm\\Python38', 
 'D:\\workspace_python\\basis_learn01\\venv', 
 'D:\\workspace_python\\basis_learn01\\venv\\lib\\site-packages', 
 'E:\\PyCharm\\PyCharm 2020.1.3\\plugins\\python\\helpers\\pycharm_matplotlib_backend']
           

 你導入一個子產品時,Python解釋器會按照上面清單順序去依次到每個目錄下去比對你要導入的子產品名,隻要在一個目錄下比對到了該子產品名,就立刻導入,不再繼續往後找。

注意清單第一個元素為空,即代表目前目錄,是以你自己定義的子產品在目前目錄會被優先導入。

 我們自己建立的子產品若想在任何地方都能調用,那就得確定你的子產品檔案至少在子產品路徑的查找清單中。

 我們一般把自己寫的子產品放在一個帶有“site-packages”字樣的目錄裡,我們從網上下載下傳安裝的各種第三方的子產品一般都放在這個目錄。

python基礎 -23- 子產品(random,chardet,os,sys,copy,time,datetime,pytz,pickle,json,MD5,SHA-1,shutil,re等)
python基礎 -23- 子產品(random,chardet,os,sys,copy,time,datetime,pytz,pickle,json,MD5,SHA-1,shutil,re等)

第3方開源子產品的安裝使用

 https://pypi.python.org/pypi 是python的開源子產品庫,截止2019年4.30日 ,已經收錄了175870個來自全世界python開發者貢獻的子產品,幾乎涵蓋了你想用python做的任何事情。 事實上每個python開發者,隻要注冊一個賬号就可以往這個平台上傳你自己的子產品,這樣全世界的開發者都可以容易的下載下傳并使用你的子產品。

 直接通過pip安裝

 pip指令會自動下載下傳子產品包并完成安裝。

 軟體一般會被自動安裝你python安裝目錄的這個子目錄裡

使用國内鏡像(暫時)

 pip指令預設會連接配接在國外的python官方伺服器下載下傳,速度比較慢,你還可以使用國内的豆瓣源,資料會定期同步國外官網,速度快好多

-i 後面跟的是豆瓣源位址

—trusted-host 得加上,是通過網站https安全驗證用的

使用國内鏡像(永久)

  • 在檔案夾的位址欄輸入 %appdata% (即進入這個檔案夾)。
  • 在目前檔案夾下建立一個pip檔案夾。
  • 進入pip檔案夾,建立一個pip.ini檔案
  • 在pip.ini檔案中寫下如下内容:
[global]
index-url=http://mirrors.aliyun.com/pypi/simple/
 
[install]
trusted-host=mirrors.aliyun.com
           

包&跨目錄導入子產品

 當你的子產品檔案越來越多,就需要對子產品檔案進行劃分,比如把負責跟資料庫互動的都放一個檔案夾,把與頁面互動相關的放一個檔案夾,

my_proj/
├── apeland_web  #代碼目錄
│   ├── __init__.py
│   ├── admin.py
│   ├── apps.py
│   ├── models.py
│   ├── tests.py
│   └── views.py
├── manage.py
└── my_proj    #配置檔案目錄
    ├── __init__.py
    ├── settings.py
    ├── urls.py
    └── wsgi.py
           

 像上面這樣,一個檔案夾管理多個子產品檔案,這個檔案夾就被稱為包

 一個包就是一個檔案夾,但該檔案夾下必須存在 init.py 檔案, 該檔案的内容可以為空, int.py用于辨別目前檔案夾是一個包。

 這個init.py的檔案主要是用來對包進行一些初始化的,當目前這個package被别的程式調用時,init.py檔案會先執行,一般為空, 一些你希望隻要package被調用就立刻執行的代碼可以放在init.py裡,一會後面會示範。

跨子產品導入

目錄結構如下

python基礎 -23- 子產品(random,chardet,os,sys,copy,time,datetime,pytz,pickle,json,MD5,SHA-1,shutil,re等)

 根據上面的結構,如何實作在

包.py

裡調用

my_package

子產品?

 直接導入的話,會報錯,說找到不子產品

python基礎 -23- 子產品(random,chardet,os,sys,copy,time,datetime,pytz,pickle,json,MD5,SHA-1,shutil,re等)

方式一

 我們自己建立的子產品若想在任何地方都能調用,那就得確定你的子產品檔案至少在子產品路徑的查找清單中。

 我們一般把自己寫的子產品放在一個帶有“site-packages”字樣的目錄裡,我們從網上下載下傳安裝的各種第三方的子產品一般都放在這個目錄。

python基礎 -23- 子產品(random,chardet,os,sys,copy,time,datetime,pytz,pickle,json,MD5,SHA-1,shutil,re等)
from my_package.test import test_p

test_p()
           

方式二

 添加環境變量,把父親級的路徑添加到sys.path中,就可以了,這樣導入 就相當于從父親級開始找子產品了。

import sys
import os

# 固定擷取目錄(不可取)
# base_dir = "D:\\workspace_python\\basis_learn01\\basicGrammar"

print(os.path.dirname(os.path.dirname(__file__)))

# 動态的擷取目錄
base_dir = os.path.dirname(os.path.dirname(__file__))
sys.path.append(base_dir)

from my_package.test import test_p

test_p()
           

方式三(官方推薦)

 雖然通過添加環境變量的方式可以實作跨子產品導入,但是官方不推薦這麼幹,因為這樣就需要在每個目錄下的每個程式裡都寫一遍添加環境變量的代碼。

 官方推薦的玩法是,在項目裡建立個入口程式,整個程式調用的開始應該是從入口程式發起,這個入口程式一般放在項目的頂級目錄

 這樣做的好處是,項目中的二級目錄 module.包.py中再調用他表親my_package.test.py時就不用再添加環境變量了。

 原因是由于manage.py在頂層,manage.py啟動時項目的環境變量路徑就會自動變成….xxx/basicGrammar/這一級别

python基礎 -23- 子產品(random,chardet,os,sys,copy,time,datetime,pytz,pickle,json,MD5,SHA-1,shutil,re等)
python基礎 -23- 子產品(random,chardet,os,sys,copy,time,datetime,pytz,pickle,json,MD5,SHA-1,shutil,re等)
其實三種方式本質上都是通過

子產品查找路徑

來跨目錄導入的

方式四(動态加載)

import importlib

__import__("basicGrammar.module.my_module")
importlib.import_module("basicGrammar.module.my_module")
           

random

#1.随機的浮點數,範圍是在0.0~1.0之間:random.random()
import random
random.random()
0.644354136192532

#2.函數随機生成一個[a,b]範圍内的浮點數:random.uniform(a, b)
random.uniform(0, 100)
24.333751706253736

#3.随機生成一個範圍[a, b]内的整數:random.randint(a, b)
random.randint(1,10)
6

#4.随機選取一個元素傳回:random.choice()
可以用于字元串、清單、元組等
random.choice([1,2,3])  #清單
3
random.choice((1,2,3))   #元組
2
random.choice("hello world")  #字元串
'h'
#随機生成字元
random.choice('[email protected]#$%^&*()')
'l'

#5.随機打亂元素:random.shuffle()
l = [1,2,3,4]
random.shuffle(l)
print(l)
[2, 4, 3, 1]

#6.從序列a中截取指定長度n的片段:random.sample(a, n)
a = [1,2,3,4,5]
b = "hello world"
n = 2
random.sample(a, n)
[5, 3]
random.sample(b, n)
['o', 'r']

#7.随機選取a到b間的奇數1/偶數2:random.randrange(a, b, 2)
random.randrange(0, 11, 1)   #奇數
5
random.randrange(0, 11, 2)   #偶數
10
           

簡單描述

  1. 随機的浮點數,範圍是在0.0~1.0之間:random.random();
  2. 函數随機生成一個[a,b]範圍内的浮點數:random.uniform(a, b);
  3. 随機生成一個範圍[a, b]内的整數:random.randint(a, b);
  4. 随機選取一個元素傳回或随機生成字元:random.choice();
  5. 随機打亂元素:random.shuffle();
  6. 從序列a中截取指定長度n的片段:random.sample(a, n);
  7. 随機選取a到b間的奇數1/偶數2:random.randrange(a, b, 2)。

chardet

 安裝

pip install chardet
           

 使用

import chardet

file = open(file="F:\\Animation\\0.txt", mode="r")
info = file.read(6)
print("編碼格式:", chardet.detect(info.encode()).get("encoding"))
file.close()
           

 輸出

os

import os

# 修改檔案的名稱
os.replace(file_new_name, file_name)   # win
# os.rename(file_new_name, file_name)  # mac
           

 更多方法

得到目前工作目錄,即目前Python腳本工作的目錄路徑: os.getcwd()
傳回指定目錄下的所有檔案和目錄名:os.listdir()
函數用來删除一個檔案:os.remove()
删除多個目錄:os.removedirs(r“c:\python”)
檢驗給出的路徑是否是一個檔案:os.path.isfile()
檢驗給出的路徑是否是一個目錄:os.path.isdir()
判斷是否是絕對路徑:os.path.isabs()
檢驗給出的路徑是否真地存:os.path.exists()
傳回一個路徑的目錄名和檔案名:os.path.split()     e.g os.path.split('/home/swaroop/byte/code/poem.txt') 結果:('/home/swaroop/byte/code', 'poem.txt') 
分離擴充名:os.path.splitext()       e.g  os.path.splitext('/usr/local/test.py')    結果:('/usr/local/test', '.py')
擷取路徑名:os.path.dirname()
獲得絕對路徑: os.path.abspath()  
擷取檔案名:os.path.basename()
運作shell指令: os.system()
讀取作業系統環境變量HOME的值:os.getenv("HOME") 
傳回作業系統所有的環境變量: os.environ 
設定系統環境變量,僅程式運作時有效:os.environ.setdefault('HOME','/home/alex')
給出目前平台使用的行終止符:os.linesep    Windows使用'\r\n',Linux and MAC使用'\n'
訓示你正在使用的平台:os.name       對于Windows,它是'nt',而對于Linux/Unix使用者,它是'posix'
重命名:os.rename(old, new) 如果命名檔案已經存在,則無法建立該檔案
替換: os.replace(src, dst) 就算命名檔案已經存在,也可以建立該檔案
建立多級目錄:os.makedirs(r“c:\python\test”)
建立單個目錄:os.mkdir(“test”)
擷取檔案屬性:os.stat(file)
修改檔案權限與時間戳:os.chmod(file)
擷取檔案大小:os.path.getsize(filename)
結合目錄名與檔案名:os.path.join(dir,filename)
改變工作目錄到dirname: os.chdir(dirname)
擷取目前終端的大小: os.get_terminal_size()
殺死程序: os.kill(10884,signal.SIGKILL)
周遊目錄中的檔案(含遞歸):os.walk() 
           
python基礎 -23- 子產品(random,chardet,os,sys,copy,time,datetime,pytz,pickle,json,MD5,SHA-1,shutil,re等)

sys

 讀取外部指令

import sys

command = sys.argv
old_area = command[1]
new_area = command[2]
print("%s---%s" % (old_area, new_area))
           

 手動運作

python基礎 -23- 子產品(random,chardet,os,sys,copy,time,datetime,pytz,pickle,json,MD5,SHA-1,shutil,re等)

 常用方法

sys.argv           指令行參數List,第一個元素是程式本身路徑
sys.exit(n)        退出程式,正常退出時exit(0)
sys.version        擷取Python解釋程式的版本資訊
sys.maxint         最大的Int值
sys.path           傳回子產品的搜尋路徑,初始化時使用PYTHONPATH環境變量的值
sys.platform       傳回作業系統平台名稱
sys.stdout.write('please:')  #标準輸出 , 引出進度條的例子, 注,在py3上不行,可以用print代替
val = sys.stdin.readline()[:-1] #标準輸入
sys.getrecursionlimit() #擷取最大遞歸層數
sys.setrecursionlimit(1200) #設定最大遞歸層數
sys.getdefaultencoding()  #擷取解釋器預設編碼
sys.getfilesystemencoding  #擷取記憶體資料存到檔案裡的預設編碼
           

copy

 深拷貝

import copy

data_copy = copy.deepcopy(data)
           

time & datetime子產品

 在平常的代碼中,我們常常需要與時間打交道。在Python中,與時間處理有關的子產品就包括:time,datetime,calendar(很少用,不講),下面分别來介紹。

 我們寫程式時對時間的處理可以歸為以下3種:

  • 時間的顯示,在螢幕顯示、記錄日志等
  • 時間的轉換,比如把字元串格式的日期轉成Python中的日期類型
  • 時間的運算,計算兩個日期間的內插補點等

time

在Python中,通常有這幾種方式來表示時間:

  1. 時間戳(timestamp), 表示的是從1970年1月1日00:00:00開始按秒計算的偏移量。例子:1554864776.161901
  2. 格式化的時間字元串,比如“2020-10-03 17:54”
  3. 元組(struct_time)共九個元素。由于Python的time子產品實作主要調用C庫,是以各個平台可能有所不同,mac上:time.struct_time(tm_year=2020, tm_mon=4, tm_mday=10, tm_hour=2, tm_min=53, tm_sec=15, tm_wday=2, tm_yday=100, tm_isdst=0)
索引(Index)    屬性(Attribute)    值(Values)
0     tm_year(年)                 比如2011
1     tm_mon(月)                  1 - 12
2     tm_mday(日)                 1 - 31
3     tm_hour(時)                 0 - 23
4     tm_min(分)                  0 - 59
5     tm_sec(秒)                  0 - 61
6     tm_wday(weekday)            0 - 6(0表示周日)
7     tm_yday(一年中的第幾天)       1 - 366
8     tm_isdst(是否是夏令時)        預設為-1
           

UTC時間

 UTC(Coordinated Universal Time,世界協調時)亦即格林威治天文時間,世界标準時間。在中國為UTC+8,又稱東8區。DST(Daylight Saving Time)即夏令時。

time子產品的方法

  • time.localtime([secs]):将一個時間戳轉換為目前時區的struct_time。若secs參數未提供,則以目前時間為準。
    • time.struct_time(tm_year=2020, tm_mon=7, tm_mday=29, tm_hour=15, tm_min=15, tm_sec=50, tm_wday=2, tm_yday=211, tm_isdst=0)
  • time.gmtime([secs]):和localtime()方法類似,gmtime()方法是将一個時間戳轉換為UTC時區(0時區)的struct_time。
  • time.time():傳回目前時間的時間戳。1596006950.818224
  • time.mktime(t):将一個struct_time轉化為時間戳。
  • time.sleep(secs):線程推遲指定的時間運作,機關為秒。
  • time.asctime([t]):把一個表示時間的元組或者struct_time表示為這種形式:’Sun Oct 1 12:04:38 2019’。如果沒有參數,将會将time.localtime()作為參數傳入。
  • time.ctime([secs]):把一個時間戳(按秒計算的浮點數)轉化為time.asctime()的形式。如果參數未給或者為None的時候,将會預設time.time()為參數。它的作用相當于time.asctime(time.localtime(secs))。
  • time.strftime(format[, t]):把一個代表時間的元組或者struct_time(如由time.localtime()和time.gmtime()傳回)轉化為格式化的時間字元串。如果t未指定,将傳入time.localtime()。
    • 舉例:time.strftime("%Y %m %d %H:%M:%S %p %j %z %Z", time.localtime())
    • 輸出:2020 07 29 15:36:17 PM 211 +0800 中國标準時間
  • time.strptime(string[, format]):把一個格式化時間字元串轉化為struct_time。實際上它和strftime()是逆操作。
    • 舉例:time.strptime(‘2017-10-3 17:54’,”%Y-%m-%d %H:%M”)
    • 輸出:time.struct_time(tm_year=2017, tm_mon=10, tm_mday=3, tm_hour=17, tm_min=54, tm_sec=0, tm_wday=1, tm_yday=276, tm_isdst=-1)
  • 字元串轉時間格式對應表
Meaning Notes

%a

Locale’s abbreviated weekday name.

%A

Locale’s full weekday name.

%b

Locale’s abbreviated month name.

%B

Locale’s full month name.

%c

Locale’s appropriate date and time representation.

%d

Day of the month as a decimal number [01,31].

%H

Hour (24-hour clock) as a decimal number [00,23].

%I

Hour (12-hour clock) as a decimal number [01,12].

%j

Day of the year as a decimal number [001,366].

%m

Month as a decimal number [01,12].

%M

Minute as a decimal number [00,59].

%p

Locale’s equivalent of either AM or PM. (1)

%S

Second as a decimal number [00,61]. (2)

%U

Week number of the year (Sunday as the first day of the week) as a decimal number [00,53]. All days in a new year preceding the first Sunday are considered to be in week 0. (3)

%w

Weekday as a decimal number [0(Sunday),6].

%W

Week number of the year (Monday as the first day of the week) as a decimal number [00,53]. All days in a new year preceding the first Monday are considered to be in week 0. (3)

%x

Locale’s appropriate date representation.

%X

Locale’s appropriate time representation.

%y

Year without century as a decimal number [00,99].

%Y

Year with century as a decimal number.

%z

Time zone offset indicating a positive or negative time difference from UTC/GMT of the form +HHMM or -HHMM, where H represents decimal hour digits and M represents decimal minute digits [-23:59, +23:59].

%Z

Time zone name (no characters if no time zone exists).

%%

A literal

‘%’

character.

 最後為了容易記住轉換關系,看下圖

python基礎 -23- 子產品(random,chardet,os,sys,copy,time,datetime,pytz,pickle,json,MD5,SHA-1,shutil,re等)

datetime

 相比于time子產品,datetime子產品的接口則更直覺、更容易調用

 datetime子產品定義了下面這幾個類:

  • datetime.date:表示日期的類。常用的屬性有year, month, day;
  • datetime.time:表示時間的類。常用的屬性有hour, minute, second, microsecond;
  • datetime.datetime:表示日期時間。
  • datetime.timedelta:表示時間間隔,即兩個時間點之間的長度。
  • datetime.tzinfo:與時區有關的相關資訊。(這裡不詳細充分讨論該類,感興趣的童鞋可以參考python手冊)

 我們需要記住的方法僅以下幾個:

  1. d=datetime.datetime.now() 傳回目前的datetime日期類型
d.timestamp(),d.today(), d.year,d.timetuple()等方法可以調用
           
  1. 時間戳和datetime日期類型的轉換
    print(datetime.datetime.now().timestamp())   # 1596018092.231518
    print(datetime.datetime.fromtimestamp(time.time()))  # 2020-07-29 18:21:32.231519
               
  2. 時間運算和時間替換
    print(datetime.datetime.now()-datetime.timedelta(days=3, minutes=2))
    print(datetime.datetime.now().replace(year=2009,month=10,day=7,minute=48,second=50))
    
    ---
    2020-07-26 18:21:22.140702
    2009-10-07 18:48:50.140702
               
  3. 時區和時間字元串轉換
    print(datetime.datetime.now().astimezone(pytz.timezone("Asia/Shanghai")).strftime("%Y %m %d %H:%M:%S"))  # 2020 07 29 18:36:00
    print(datetime.datetime.now().astimezone(pytz.timezone("Africa/Asmara")).strftime("%Y %m %d %H:%M:%S"))  # 2020 07 29 13:36:00
               

pytz(時區)

 安裝

pip install pytz
           
import pytz

# 檢視所有時區
print(pytz.all_timezones)
print(type(pytz.timezone("Asia/Shanghai")))
print(datetime.datetime.now().astimezone(pytz.timezone("Asia/Shanghai")).strftime("%Y %m %d %H:%M:%S"))
print(datetime.datetime.now().astimezone(pytz.timezone("Africa/Asmara")).strftime("%Y %m %d %H:%M:%S"))
           

 輸出

['Africa/Abidjan', 'Africa/Accra', 'Africa/Addis_Ababa', 'Africa/Algiers', 'Africa/Asmara', .....]
<class 'pytz.tzfile.Asia/Shanghai'>
2020 07 29 18:36:55
2020 07 29 13:36:55
           

pickcle & json(序列化)

什麼叫序列化?

 序列化是指把記憶體裡的資料類型轉變成字元串,以使其能存儲到硬碟或通過網絡傳輸到遠端,因為硬碟或網絡傳輸時隻能接受bytes

為什麼要序列化?

 你打遊戲過程中,打累了,停下來,關掉遊戲、想過2天再玩,2天之後,遊戲又從你上次停止的地方繼續運作,你上次遊戲的進度肯定儲存在硬碟上了,是以何種形式呢?遊戲過程中産生的很多臨時資料是不規律的,可能在你關掉遊戲時正好有10個清單,3個嵌套字典的資料集合在記憶體裡,需要存下來?你如何存?把清單變成檔案裡的多行多列形式?那嵌套字典呢?根本沒法存。是以,若是有種辦法可以直接把記憶體資料存到硬碟上,下次程式再啟動,再從硬碟上讀回來,還是原來的格式的話,那是極好的。

 用于序列化的兩個子產品

  • json,用于字元串 和 python資料類型間進行轉換
  • pickle,用于python特有的類型 和 python的資料類型間進行轉換

pickle

 pickle子產品提供了四個功能:dumps、dump、loads、load

 dumps和loads

print(pickle.dumps(info))
print(pickle.dumps(name))

print(pickle.loads(b"\x80\x04\x95\x1b\x00\x00\x00\x00\x00\x00\x00}\x94(\x8c\x04name\x94\x8c\x04Alex\x94\x8c\x03age\x94K\x14u."))
print(pickle.loads(b"\x80\x04\x95\x08\x00\x00\x00\x00\x00\x00\x00\x8c\x04Alex\x94."))
           

 輸出

b'\x80\x04\x95\x1b\x00\x00\x00\x00\x00\x00\x00}\x94(\x8c\x04name\x94\x8c\x04Alex\x94\x8c\x03age\x94K\x14u.'
b'\x80\x04\x95\x08\x00\x00\x00\x00\x00\x00\x00\x8c\x04Alex\x94.'
{'name': 'Alex', 'age': 20}
Alex
           

 dump和load

file = open("F:\\Animation\\10.txt", "wb")
pickle.dump(info, file)
pickle.dump(name, file)
file.close()

"F:\\Animation\\10.txt", "wb"
file = open("F:\\Animation\\10.txt", "rb")
print(pickle.load(file))  # {'name': 'Alex', 'age': 20}
print(pickle.load(file))  # Alex
file.close()
           

 輸出

{'name': 'Alex', 'age': 20}
Alex
           
python基礎 -23- 子產品(random,chardet,os,sys,copy,time,datetime,pytz,pickle,json,MD5,SHA-1,shutil,re等)

json

 Json子產品也提供了四個功能:dumps、dump、loads、load,用法跟pickle一緻

 dumps和loads

print(json.dumps(name))  # 注意json dumps生成的是字元串,不是bytes
print(json.dumps(info))
print(json.loads(json.dumps(info)))
           

 輸出

"Alex"
{"name": "Alex", "age": 20}
{'name': 'Alex', 'age': 20}
           

 dump和load

需要注意的一點是json在檔案中序列化的資料最好是字典的格式。因為讀取資料的時候隻能以

字典的資料結構

取出一次。
# 因為json的load隻能load一次,将資料轉為字典。是以序列化的資料隻能是字典
file = open("F:\\Animation\\10.txt", "w")
info.setdefault("姓名", name)
json.dump(info, file)
file.close()

file = open("F:\\Animation\\10.txt", "r")
print(json.load(file))
file.close()
           

 輸出

python基礎 -23- 子產品(random,chardet,os,sys,copy,time,datetime,pytz,pickle,json,MD5,SHA-1,shutil,re等)

json與pickle的比較

JSON:

  • 優點:跨語言(不同語言間的資料傳遞可用json交接)、體積小
  • 缺點:隻能支援int\str\list\tuple\dict

Pickle:

  • 優點:專為python設計,支援python所有的資料類型
  • 缺點:隻能在python中使用,存儲資料占空間大
其實就是Json和序列化的一個比較

加密算法

hash

 Hash,一般翻譯做“散列”,也有直接音譯為”哈希”的,就是把任意長度的輸入(又叫做預映射,pre-image),通過雜湊演算法,變換成固定長度的輸出,該輸出就是散列值。這種轉換是一種壓縮映射,也就是,散列值的空間通常遠小于輸入的空間,不同的輸入可能會散列成相同的輸出,而不可能從散列值來唯一的确定輸入值。

 簡單的說就是一種将任意長度的消息壓縮到某一固定長度的消息摘要的函數。

 HASH主要用于資訊安全領域中加密算法,他把一些不同長度的資訊轉化成雜亂的128位的編碼裡,叫做HASH值.也可以說,hash就是找到一種資料内容和資料存放位址之間的映射關系

MD5

什麼是MD5算法

 MD5訊息摘要演算法(英語:MD5 Message-Digest Algorithm),一種被廣泛使用的密碼雜湊函數,可以産生出一個128位的散列值(hash value),用于確定資訊傳輸完整一緻。MD5的前身有MD2、MD3和MD4。

MD5功能

 輸入任意長度的資訊,經過處理,輸出為128位的資訊(數字指紋);

 不同的輸入得到的不同的結果(唯一性);

MD5算法的特點

  1. 壓縮性:任意長度的資料,算出的MD5值的長度都是固定的
  2. 容易計算:從原資料計算出MD5值很容易
  3. 抗修改性:對原資料進行任何改動,修改一個位元組生成的MD5值差別也會很大
  4. 強抗碰撞:已知原資料和MD5,想找到一個具有相同MD5值的資料(即僞造資料)是非常困難的。

MD5算法是否可逆?

 MD5不可逆的原因是其是一種散列函數,使用的是hash算法,在計算過程中原文的部分資訊是丢失了的。

MD5用途

  1. 防止被篡改:
    • 比如發送一個電子文檔,發送前,我先得到MD5的輸出結果a。然後在對方收到電子文檔後,對方也得到一個MD5的輸出結果b。如果a與b一樣就代表中途未被篡改。
    • 比如我提供檔案下載下傳,為了防止不法分子在安裝程式中添加木馬,我可以在網站上公布由安裝檔案得到的MD5輸出結果。
    • SVN在檢測檔案是否在CheckOut後被修改過,也是用到了MD5.
  2. 防止直接看到明文:
    • 現在很多網站在資料庫存儲使用者的密碼的時候都是存儲使用者密碼的MD5值。這樣就算不法分子得到資料庫的使用者密碼的MD5值,也無法知道使用者的密碼。(比如在UNIX系統中使用者的密碼就是以MD5(或其它類似的算法)經加密後存儲在檔案系統中。當使用者登入的時候,系統把使用者輸入的密碼計算成MD5值,然後再去和儲存在檔案系統中的MD5值進行比較,進而确定輸入的密碼是否正确。通過這樣的步驟,系統在并不知道使用者密碼的明碼的情況下就可以确定使用者登入系統的合法性。這不但可以避免使用者的密碼被具有系統管理者權限的使用者知道,而且還在一定程度上增加了密碼被破解的難度。)
  3. 防止抵賴(數字簽名):
    • 這需要一個第三方認證機構。例如A寫了一個檔案,認證機構對此檔案用MD5算法産生摘要資訊并做好記錄。若以後A說這檔案不是他寫的,權威機構隻需對此檔案重新産生摘要資訊,然後跟記錄在冊的摘要資訊進行比對,相同的話,就證明是A寫的了。這就是所謂的“數字簽名”。

使用

import hashlib

md5 = hashlib.md5()
md5.update("你好呀".encode("utf-8"))
print(md5.digest())
print(md5.hexdigest())

md5.update("你好呀".encode("utf-8"))  # 相加
print(md5.digest())
print(md5.hexdigest())

md5_2 = hashlib.md5("你好呀".encode("utf-8"))
print(md5_2.hexdigest())
           

 輸出

b'Oe\xfd\xb3>\x0f+\xd0\xdek\xd1\xb4\x1f\xde\xa9h'
4f65fdb33e0f2bd0de6bd1b41fdea968
b'\x0e\x10\xe6\xe2.\x82_\x9e\xbfe\xa7\x12\xde\x80\xd0\xec'
0e10e6e22e825f9ebf65a712de80d0ec
4f65fdb33e0f2bd0de6bd1b41fdea968
           
一般還需要加鹽

SHA-1

 安全雜湊演算法(Secure Hash Algorithm)主要适用于數字簽名标準(Digital Signature Standard DSS)裡面定義的數字簽名算法(Digital Signature Algorithm DSA)。對于長度小于2^64位的消息,SHA1會産生一個160位的消息摘要。當接收到消息的時候,這個消息摘要可以用來驗證資料的完整性。

 SHA是美國國家安全局設計的,由美國國家标準和技術研究院釋出的一系列密碼散列函數。

 由于MD5和SHA-1于2005年被山東大學的教授王小雲破解了,科學家們又推出了SHA224, SHA256, SHA384, SHA512,當然位數越長,破解難度越大,但同時生成加密的消息摘要所耗時間也更長。目前最流行的是加密算法是SHA-256 .

使用

print("-----SH1-----")
print(hashlib.sha1("你好呀".encode("utf-8")).hexdigest())
print(hashlib.sha1("你好呀".encode("utf-8")).hexdigest())

print("-----SH256-----")
print(hashlib.sha256("你好呀".encode("utf-8")).hexdigest())
print(hashlib.sha256("你好呀".encode("utf-8")).hexdigest())
           

 輸出

-----SH1-----
06161148546813f145022d12c7df76b8546edcea
06161148546813f145022d12c7df76b8546edcea
-----SH256-----
d2033138c3b3be1321ad29d0aff15a4b1b47934a2b91afcea6f59b96a9fed115
d2033138c3b3be1321ad29d0aff15a4b1b47934a2b91afcea6f59b96a9fed115
           

MD5與SHA-1的比較

 由于MD5與SHA-1均是從MD4發展而來,它們的結構和強度等特性有很多相似之處,SHA-1與MD5的最大差別在于其摘要比MD5摘要長32 比特。對于強行攻擊,産生任何一個封包使之摘要等于給定封包摘要的難度:MD5是2128數量級的操作,SHA-1是2160數量級的操作。産生具有相同摘要的兩個封包的難度:MD5是264是數量級的操作,SHA-1 是280數量級的操作。因而,SHA-1對強行攻擊的強度更大。但由于SHA-1的循環步驟比MD5多80:64且要處理的緩存大160比特:128比特,SHA-1的運作速度比MD5慢。

shutil & zipfile & tarfile

進階的 檔案、檔案夾、壓縮包 處理子產品

shutil.copyfileobj(fsrc, fdst[, length])

将檔案内容拷貝到另一個檔案中

shutil.copyfile(src, dst)

拷貝檔案

shutil.copymode(src, dst)

僅拷貝權限。内容、組、使用者均不變

shutil.copystat(src, dst)

僅拷貝狀态的資訊,包括:mode bits, atime, mtime, flags

shutil.copy(src, dst)

拷貝檔案和權限

shutil.copy2(src, dst)

拷貝檔案和狀态資訊

shutil.ignore_patterns(*patterns)

shutil.copytree(src, dst, symlinks=False, ignore=None)

遞歸的去拷貝檔案夾

shutil.rmtree(path[, ignore_errors[, onerror]])

遞歸的去删除檔案

shutil.move(src, dst)

遞歸的去移動檔案,它類似mv指令,其實就是重命名。

shutil.make_archive(base_name, format,…)

建立壓縮包并傳回檔案路徑,例如:zip、tar

可選參數如下:

  • base_name: 壓縮包的檔案名,也可以是壓縮包的路徑。隻是檔案名時,則儲存至目前目錄,否則儲存至指定路徑,

如 data_bak =>儲存至目前路徑

如:/tmp/data_bak =>儲存至/tmp/

  • format: 壓縮包種類,“zip”, “tar”, “bztar”,“gztar”
  • root_dir: 要壓縮的檔案夾路徑(預設目前目錄)
  • owner: 使用者,預設目前使用者
  • group: 組,預設目前組
  • logger: 用于記錄日志,通常是logging.Logger對象
#将 /data 下的檔案打包放置目前程式目錄
import shutil
ret = shutil.make_archive("data_bak", 'gztar', root_dir='/data')

#将 /data下的檔案打包放置 /tmp/目錄
import shutil
ret = shutil.make_archive("/tmp/data_bak", 'gztar', root_dir='/data')
           

shutil 對壓縮包的處理是調用 ZipFile 和 TarFile 兩個子產品來進行的,詳細:

zipfile壓縮&解壓縮

import zipfile
# 壓縮
z = zipfile.ZipFile('laxi.zip', 'w')
z.write('a.log')
z.write('data.data')
z.close()

# 解壓
z = zipfile.ZipFile('laxi.zip', 'r')
z.extractall(path='.')
z.close()
           

tarfile壓縮&解壓縮

import tarfile
# 壓縮
>>> t=tarfile.open('/tmp/egon.tar','w')
>>> t.add('/test1/a.py',arcname='a.bak')
>>> t.add('/test1/b.py',arcname='b.bak')
>>> t.close()

# 解壓
>>> t=tarfile.open('/tmp/egon.tar','r')
>>> t.extractall('/egon')
>>> t.close()
           

 自定義壓縮

import zipfile
import os


zip_file = zipfile.ZipFile("F:\\Zip.zip","w")
file_list = []
for root_dir,dirs,files in os.walk("F:\\Animation"):
    print(root_dir,dirs,files)
    for file_name in files:
       file_list.append(os.path.join(root_dir, file_name))

for file in file_list:
    zip_file.write(file)


zip_file.close()
           

re(正規表達式)

引子

 請從以下檔案裡取出所有的手機号

姓名        地區    身高    體重    電話
況詠蜜     北京    171    48    13651054608
王心顔     上海    169    46    13813234424
馬纖羽     深圳    173    50    13744234523
喬亦菲     廣州    172    52    15823423525
羅夢竹     北京    175    49    18623423421
劉諾涵     北京    170    48    18623423765
嶽妮妮     深圳    177    54    18835324553
賀婉萱     深圳    174    52    18933434452
葉梓萱    上海     171    49    18042432324
杜姗姗   北京      167    49    13324523342
           

 你能想到的辦法是什麼?

 必然是下面這種吧?

f = open("兼職白領學生空姐模特護士聯系方式.txt",'r',encoding="gbk")
phones = []

for line in f:
    name,city,height,weight,phone = line.split()
    if phone.startswith('1') and len(phone) == 11:
        phones.append(phone)
        
print(phones)
           

 有沒有更簡單的方式?

 手機号是有規則的,都是數字且是11位,再嚴格點,就都是1開頭,如果能把這樣的規則寫成代碼,直接拿規則代碼比對檔案内容不就行了?

file = open("F:\\Animation\\2.txt", "r")
file_info = file.read()
find_info = re.findall("[0-9]{11}", file_info)
print("類型:{0},資料:{1}".format(type(find_info), find_info))
           

 輸出

re

 正規表達式就是字元串的比對規則,在多數程式設計語言裡都有相應的支援,python裡對應的子產品是re

常用的表達式規則

'.'     預設比對除\n之外的任意一個字元,若指定flag DOTALL,則比對任意字元,包括換行
'^'     比對字元開頭,若指定flags MULTILINE,這種也可以比對上(r"^a","\nabc\neee",flags=re.MULTILINE)
'$'     比對字元結尾, 若指定flags MULTILINE ,re.search('foo.$','foo1\nfoo2\n',re.MULTILINE).group() 會比對到foo1
'*'     比對*号前的字元0次或多次, re.search('a*','aaaabac')  結果'aaaa'
'+'     比對前一個字元1次或多次,re.findall("ab+","ab+cd+abb+bba") 結果['ab', 'abb']
'?'     比對前一個字元1次或0次 ,re.search('b?','alex').group() 比對b 0次
'{m}'   比對前一個字元m次 ,re.search('b{3}','alexbbbs').group()  比對到'bbb'
'{n,m}' 比對前一個字元n到m次,re.findall("ab{1,3}","abb abc abbcbbb") 結果'abb', 'ab', 'abb']
'|'     比對|左或|右的字元,re.search("abc|ABC","ABCBabcCD").group() 結果'ABC'
'(...)' 分組比對, re.search("(abc){2}a(123|45)", "abcabca456c").group() 結果為'abcabca45'
'\A'    隻從字元開頭比對,re.search("\Aabc","alexabc") 是比對不到的,相當于re.match('abc',"alexabc") 或^
'\Z'    比對字元結尾,同$ 
'\d'    比對數字0-9
'\D'    比對非數字
'\w'    比對[A-Za-z0-9]
'\W'    比對非[A-Za-z0-9]
's'     比對空白字元、\t、\n、\r , re.search("\s+","ab\tc1\n3").group() 結果 '\t'
'(?P...)' 分組比對 re.search("(?P[0-9]{4})(?P[0-9]{2})(?P[0-9]{4}
           

re的比對文法有以下幾種

  • re.compile 指定一個比對規則
  • re.match 從頭開始比對
  • re.search 比對包含
  • re.findall 把所有比對到的字元放到以清單中的元素傳回
  • re.split 以比對到的字元當做清單分隔符
  • re.sub 比對字元并替換
  • re.fullmatch 全部比對

Flags标志符

  • re.I(re.IGNORECASE): 忽略大小寫(括号内是完整寫法,下同)
  • re.M(MULTILINE): 多行模式,改變’^’和’$’的行為
  • re.S(DOTALL): 改變’.’的行為,make the ‘.’ special character match any character at all, including a newline; without this flag, ‘.’ will match anything except a newline.
  • re.X(re.VERBOSE) 可以給你的表達式寫注釋,使其更可讀,下面這2個意思一樣
a = re.compile(r"""\d + # the integral part
                \. # the decimal point
                \d * # some fractional digits""", 
                re.X)
b = re.compile(r"\d+\.\d*")
           

group

 可以拿到括号中比對的值

print(re.match("(AB|CD|EF|GH|I|12|54)(AB|CD|EF|GH|I|12|54)", "ABCDEFGHI123456").groups())
print(re.match("(AB|CD|EF|GH|I|12|54)(AB|CD|EF|GH|I|12|54)", "ABCDEFGHI123456").group())
           

 輸出

('AB', 'CD')
ABCD
           

re.compile(pattern, flags=0)

 Compile a regular expression pattern into a regular expression object, which can be used for matching using its match(), search() and other methods, described below.

 例子

compile_email = re.compile('\[email protected]\w+\.(com|cn|edu)')
print(compile_email.match("[email protected]"))
           

 輸出

 等同于

re.match(pattern, string, flags=0)

從起始位置開始根據模型去字元串中比對指定内容,比對單個

  • pattern 正規表達式
  • string 要比對的字元串
  • flags 标志位,用于控制正規表達式的比對方式

 輸出

re.search(pattern, string, flags=0)

 根據模型去字元串中比對指定内容,比對單個

 輸出

re.findall(pattern, string, flags=0)

 match and search均用于比對單值,即:隻能比對字元串中的一個,如果想要比對到字元串中所有符合條件的元素,則需要使用 findall。

file = open("F:\\Animation\\2.txt", "r")
file_info = file.read()
find_info = re.findall("[0-9]{11}", file_info)
print("類型:{0},資料:{1}".format(type(find_info), find_info))
           

 輸出

re.sub(pattern, repl, string, count=0, flags=0)

 用于替換比對的字元串,比str.replace功能更加強大

print(re.sub("(ldh|zxy)+", "*", "dsaldhdjsiaolzxydasldhdusazxy"))
print(re.sub("(ldh|zxy)+", "*", "dsaldhdjsiaolzxydasldhdusazxy", count=2))
           

 輸出

dsa*djsiaol*das*dusa*
dsa*djsiaol*dasldhdusazxy
           

re.split(pattern, string, maxsplit=0, flags=0)

 用比對到的值做為分割點,把值分割成清單

 輸出

re.fullmatch(pattern, string, flags=0)

 整個字元串比對成功就傳回re object, 否則傳回None

 輸出