正則表達式,只要是和計算機有必定接觸的,或多或少都能知道正則表達式,它能快讀幫忙匹配到相應的字符,以致於咱們經常使用的execl,word,文件搜索工具等均可以看到正則的影子,正則的發明不亞於鼠標鍵盤的發明,並且學習計算機一定會接觸到正則表達式,並且正則表達式的規則在任何一種語言裏面都存在,並且是相同的。html
#判斷用戶輸入的電話號碼是否合規 import re phone_number=input('請輸入有效的電話號碼:') if re.match('^(13|15|18)[0-9]{9}$',phone_number): #手機號碼必須是13,15,18開頭,必須是數字類型,必須知足11位({9}這個是減掉了13,15,18這兩位) print('是有效的手機號碼') else: print('無效的手機號碼')
上面的案例,若是是用for循環去寫就會重複寫不少代碼,且讀起來也麻煩,使用正則就會很方便,精簡到一行中完成了對號碼檢驗的工做。java
正則表達式 | 查詢字符 | 查詢結果 | 表達式說明 |
123456789 | 8 | 8 | 匹配1到9數字裏面的任意一個數字 |
1234567 | a | 無 | 數字只能匹配數字 |
[0-9] | 5 | 5 | [0-9]和0123456789是一個意思,就是0到9 |
[a-z] | c | c | 匹配小寫的26個英文字母,也就是a到z,一樣字母只能匹配字母 |
[A-Z] | C | C | 和上面的小寫相反,匹配大寫的26個英文字母 |
[0-9a-kA-K] | 12cd_FD | 12cdFD | 能夠匹配數字,大小寫字母a-K,驗證16進制,可是不能匹配下劃線,要連下劃線也要匹配可使用\w |
字符python
正則元字符 | 匹配內容 | 匹配結果 | 表達式說明 |
. | !@#$%^&*90()=-+[]{}\/az | !@#$%^&*90()=-+[]{}\/az | 匹配除換行符之外的任意字符 |
\w | !@#$%^&*90()=-+[]{}\/az_ | 90az_ | 匹配任意字母數字下劃線 |
\s | !@#$%^&*90() =-+[]{}\/az_ | 空格 | 匹配任意的空白符 |
\d | !@#$%^&*90() =-+[]{}\/az_ | 90 | 匹配任意數字 |
\n | !@#$%^&*90() =-+[]{}\/az_ | 換行符 | 匹配換行符 |
\t | !@#$%^&*90() =-+[]{}\/az_ | 製表符 | 匹配製表符,就是鍵盤上的tab鍵 |
\b | a | 匹配到2個結果 | 匹配一個單詞的結尾,也就是邊界符 |
^ | !@#$%^&*90() =-+[]{}\/az_ | 匹配到1個結果 | 匹配字符串的開始(重要) |
$ | !@#$%^&*90() =-+[]{}\/az_ | 匹配到1個結果 | 匹配字符串的結尾(重要) |
\W | !@#$%^&*90() =-+[]{}\/az_ | !@#$%^&*() =-+[]{}\_ | 匹配非字母數字下劃線 |
\S | !@#$%^&*90() =-+[]{}\/az_ | !@#$%^&*90()=-+[]{}\/az_ | 匹配非空白符 |
\D | !@#$%^&*90() =-+[]{}\/az_ | !@#$%^&*() =-+[]{}\/az_ | 匹配非數字 |
a|b | abcdf!@@# | ab | 匹配a或者b,|表明或的意思 |
() | 1333331555522214444 | 133331555514444 | p匹配括號內的表達式,還表示一個組 |
[123] | 123344553 | 12333 | [中括號裏面能夠指定要事先匹配的內容,超過中括號的內容不能匹配] |
[^123] | 123344553 | 4455 | 和上面相反,匹配非括號事先指定的內容 |
在a|b或的正則裏面,經常使用的場景是當一個條件不匹配前面一個就匹配後面一個,因此通常都把匹配比重高且複雜的放前面,比重小短的放後面。linux
量詞正則表達式
正則量詞 | 匹配內容 | 匹配結果 | 表達式說明 |
* | 123 | 3個結果 | 匹配零次或屢次,*號必須和其它配套使用,不能單獨出現 |
+ | 123 | 2個結果 | 匹配1次或屢次,+號必須和其它配套使用,不能單獨出現 |
? | 123 | 1個結果 | 匹配零次或1次,?號必須和其它配套使用,不能單獨出現 |
{n} | 123 | 3個結果 | 匹配n次,配套使用,不能單獨出現 |
{n,} | 123 | 3個結果 | 匹配n次或屢次, 配套使用,不能單獨出現 |
{n,m} | 123 | 3個結果 | 匹配n次或m次, 配套使用,不能單獨出現 |
若是量詞出如今[]號裏面就不須要進行轉義,由於在[]號字符組裏面標示只能匹配一次,這樣量詞*,?這樣的出如今字符組裏面就沒有正則的意義了,若是是要計算[1-9]1減去9這樣的計算,-號是須要加\斜槓轉義的。json
.^$符號api
正則表達式 | 匹配內容 | 匹配結果 | 表達式說明 |
1. | 12314133 | 121314 | 匹配全部1.的字符,就是1開頭再加1後面的任意一個字符 |
^1. | 12314133 | 12 | 匹配必須是以1開頭後面1個字符 |
1.$ | 12 | 12 | 匹配1後面1個字符,意味着只能匹配兩個,後面還多一個都不能匹配 |
其它符號安全
正則表達式 | 匹配內容 | 匹配結果 | 表達式說明 |
1.? | 1234123413 | 121213 | ?表示匹配零次或一次,即只匹配"1"後面一個任意字符 |
1.* | 1234123413 | 1234123413 | *表示匹配零次或屢次,即匹配"1"後面零個和多個任意字符 |
1.+ | 1234123413 | 1234123413 | +表示匹配1次或屢次,即匹配"1"後面1個和多個任意字符 |
1.{1,2} | 1234123413 | 12312313 | 1.表示匹配1後面任意1個字符,可是{1,2}就限制1後面再匹配兩個任意字符 |
1.*? | 123 | 1 | s上面的*,+,.,?,等單獨使用都是貪婪匹配,只要其中一個和?號組隊了就變成了惰性匹配 |
惰性匹配就是經可能的匹配最少最短的內容,貪婪匹配就是經可能的匹配最多的內容。網絡
字符集app
正則表達式 | 匹配內容 | 匹配結果 | 表達式說明 |
1[123]* | 1234512456 | 12312 | 表示匹配"1"後面[123]的字符任意次 |
1[^123]* | 12345156456 | 1156456 | 表示除了123以外的1後面的字符任意次 |
[\d] | 12345sz | 12345 | 匹配任意一個數字,和[0-9]同樣 |
[\d]+ | 12az345sz1!@1 | 1234511 | 匹配任意數字,和[0-9]同樣 |
分組
正則表達式 | 匹配內容 | 匹配結果 | 表達式說明 |
^[1-9]\d{13,16}[0-9x]$ | 1234567890123451 | 1234567890123451 | 匹配一個身份證號碼 ,可是也能夠是任意的16位數字 |
^[1-9]\d{14}(\d{2}[0-9x])?$ | 1234567890123451 | 無 | 經過()將其分組,前面必須是1到9的數字知足14位以後,後面分組也要知足兩位1-9的數字或者字母x。 |
^([1-9]\d{16}[0-9x]|[1-9]\d{14})$ | 123456789123456 | 123456789123456 | 表示先匹配[1-9]\d{16}[0-9x]若是沒有匹配上就匹配[1-9]\d{14} |
轉義符\
正則表達式 | 匹配內容 | 匹配結果 | 表達式說明 |
\\n | \n | \n | \n自己表明換行符,若是要顯示\n就須要加一個\,轉譯一下 |
"\\\\n" | '\\n' | \ | 若是在python中,字符串中的'\'也須要轉義,因此每個字符串'\'又須要轉義一次 |
r"\\\n" | \\\n | \\\n | 在字符串以前加r,讓整個字符串不轉義 |
貪婪匹配
正則表達式 | 匹配內容 | 匹配結果 | 表達式說明 |
<.*> | <12233><123> | <12233><123> | 默認爲貪婪匹配模式,會匹配儘可能長的字符串 |
<.*?> | <12233><123> | <12233><123> | 加上?爲將貪婪匹配模式轉爲非貪婪匹配模式,會匹配儘可能短的字符串 |
非貪婪匹配
正則表達式 | 匹配內容 | 匹配結果 | 表達式說明 |
[1*?] | 12323 | 1 | 匹配任意次,可是儘量的少的匹配 |
[12+?] | 12323 | 122 | 匹配1次或屢次,可是儘量的少的匹配 |
[2??] | 12323 | 22 | 匹配l零次或1次,可是儘量的少的匹配 |
[1,2]? | 12323 | 122 | 匹配n次或m次,可是儘量少的匹配 |
[1,]? | 12323 | 1 | 匹配n次以上,可是儘量少的匹配 |
量詞後面加?號就標識惰性匹配(非貪婪匹配),其中.*?是惰性匹配最經常使用的模式,.*?123標示一直匹配到123就中止。
上述只是正則表達式的一些基本狀況,也是平時會用到的部分,固然也須要咱們不斷的學習正則表達式,正則是在任何語言都存在的功能,屬於通用類的內容。
在python中若是須要去匹配上述的特殊字符能夠經過r來完成轉義的工做
ret=re.findall(r'\\s',r'\s') print(ret) 執行結果: ['\\s']
要學習python中的正則須要導入re模塊,否則是沒法使用的。
re模塊中三個重要的方法:
re模塊中findall方法
import re ret=re.findall('[a-z]+','abc bac cba') #格式('[a-z]+','abc bac cba')'第一組引號裏面是查找的內容',’後面一組引號是要匹配的內容' #在'abc bac cba'裏面查找[a到z]+任意次 print(ret) #執行結果: ['abc', 'bac', 'cba'] 能夠看到結果都放在了列表中 ret=re.findall('a','abc bac cba') #匹配abc bac cba中全部的a print(ret) 執行結果: ['a', 'a', 'a']
findall(返回全部匹配的結果放在列表中)
findall中的優先查詢
ret=re.findall('www.(baidu|qq).com','www.qq.com') #想獲得的結果是www.qq.com,由於baidu和qq之間用的|或 print(ret) #執行結果 ['qq']#結果是隻取到了qq #爲何會獲得這個結果,就是findall是沒有group的分組機制,因此它只能優先匹配分組(baidu|qq)分組裏面的內容 ret=re.findall('www.(?:baidu|qq).com','www.qq.com') print(ret) #執行結果: ['www.qq.com'] #若是在分組前面加上?:就是取消分組優先 ?號在正則裏面標識匹配0次或者1次,在.後面出現的?號就是惰性匹配,匹配到了就結束
在python re模塊中(分組優先級的概念),可是在正則裏面分組是用於對字符總體進行約束
re模塊中search方法
ret=re.search('a','abc bac cba') print(ret) #執行結果: #<_sre.SRE_Match object; span=(0, 1), match='a'> #能夠看到不加group返回的就是結果的對象 ret=re.search('a','abc bac cba').group() print(ret) 執行結果: a 只有加了group以後才能正常返回結果 ret=re.search('a','abc bac cba') print(ret.group()) 執行結果: a ret=re.search('h','abc bac cba') print(ret) print(ret.group()) 執行結果: None 能夠看到print(ret)的時候,返回的是none,由於h在要找的字符中找不到。 File "C:/pycharm_fill/正則表達式.py", line 30, in <module> print(ret.group()) AttributeError: 'NoneType' object has no attribute 'group' #後面在調用group的時候直接就報錯了,這是由於group是沒有none方法的,因此就報錯了
# ret=re.search('a','abc bac cba') # if ret: # print(ret.group()) # 執行結果: # a ret=re.search('h','abc bac cba') if ret: print(ret.group()) else: print('沒有找到該字符') #能夠看到找不到內容的時候就不會有報錯內容產生了,這個時候能夠加一次不能找到結果的輸出,固然也能夠不須要else的部分 執行結果: 沒有找到該字符
search (從前日後匹配,找到一個就返回,返回的變量須要調用group的方法,否則返回的就是變量的對象,若是沒有找到的狀況下,就會返回None,None是沒有group的方法的,結果就會報錯)
search若是是正則用的分組,那麼要取分組的內容,可使用group(參數)來獲取分組的內容,group(1)就表明取第一個分組裏面的內容,若是是給分組命名了,那麼就直接取分組名便可
分組命名:
ret=re.search('\d(?P<命名>123)+','123456qasz') ?p<>是分組命名的固定格式,<>號裏面寫分組名 取分組的內容直接,ret.group(命名)
分組命名引用:
# html1="<h1>hellopython123</h1>" # ret=re.search("<(?P<name>\w+)>\w+</(?P=name)>",html1) # print(ret.group('name')) #上面我對分組進行了命名,後面又用了一次一樣的命名,若是在代碼裏面先後的內容 # #接近,就可使用分組命名以後在後面應用一次分組名,可是後面引用的分組名必須和前面的命名一致。 # print(ret.group()) # 執行結果 # h1 #分組裏面的內容 # <h1>hellopython123</h1> #整理內容 html1="<h1>hellopython123</h1>" ret=re.search(r"<(\w+)>\w+</\1>",html1) #也能夠直接定義分組,後面按照位置來引用分組,也能夠實現上面的效果 print(ret.group(1)) #若是是分組較多的狀況下,仍是使用分組命名,可讀性高點 print(ret.group()) 執行結果: h1 <h1>hellopython123</h1>
分組命名和應用必須在一條正則裏面完成。
re模塊中match的方法:
ret=re.match('h','abc bac cba') if ret: print(ret.group()) else: print('沒有找到該字符') # 執行結果: # 沒有找到該字符 ret=re.match('a','abc bac cba') if ret: print(ret.group()) else: print('沒有找到該字符') # 執行結果: # a ret=re.match('c','abc bac cba') if ret: print(ret.group()) else: print('沒有找到該字符') # 執行結果: # 沒有找到該字符 ret=re.match('ab','abc bac cba') if ret: print(ret.group()) else: print('沒有找到該字符') 執行結果: ab
match(match是從頭開始匹配,若是正則的規則是從頭開始能匹配上就返回一個變量,就是說整個字符串裏面,正則恰好匹配了一個字符就返回了,有點像正則裏面的^a以a開始,不同的是字符串即便有多個a,可是match匹配上第一個了就返回了,並且match也是須要調用group的,同時找不到返回None的時候調用group的時候就會報錯)
search是查找,match是匹配,兩個的是與必定區別的
search和match中的group方法:
ret=re.search('^[1-9](\d{14})(\d{2}[0-9x])?$','129535188108171312') #129535188108171312是身份證 print(ret.group(1)) #這個是匹配了(\d{14})這個括號裏面的內容 print(ret.group(2)) #這個是匹配了(\d{2}[0-9x])這個括號裏面的內容 執行結果: 29535188108171 312 #能夠看到咱們在group裏面加了指,獲得的結果是將身份證進行了拆分,從兩個括號裏面進行的拆分 group能夠在例如我要取身份證後4位和前面全部的內容,分組顯示,這個就是須要用到group的功能了,可是也只能在match和search中使用
上面是re模塊的最經常使用的三種方法。
re模塊中spilt的方法:
ret=re.split('ab','abcd') #split的用法是,先按照查找的a匹配一次取到bcd,而後再按照bcd取一次,獲得結果cd print(ret) 執行結果: ['', 'cd'] ret=re.split('ab','abc bca cba') #若是字符串是在一行的話,即便中間喲空格也是當作一行處理 print(ret) 執行結果 ['', 'c bca cba']
spilt中的優先查詢
ret=re.split('(\d+)','abc123bca456cab') #在spilt中也是,若是加了分組(),就會在切的時候還給保留切分的部分 print(ret) #執行結果 #['abc', '123', 'bca', '456', 'cab'] #反之,不用分組就正常切 ret=re.split('\d+','abc123bca456cab') print(ret) #執行結果 ['abc', 'bca', 'cab']
re模塊中sub的方法:
ret=re.sub('\d','K','abc1 bca2 cba3') #將'abc1 bca2 cba3'裏面的數字(\d)替換成k print(ret) 執行結果: abcK bcaK cbaK ret=re.sub('\d','K','abc1 bca2 cba3',1) #將'abc1 bca2 cba3'裏面的數字(\d)替換成k,只替換一個 print(ret) 執行結果: abcK bca2 cba3
re模塊中subn的方法:
ret=re.subn('\d','K','abc1 bca2 cba3') # #將'abc1 bca2 cba3'裏面的數字(\d)替換成k print(ret) # 執行結果: ('abcK bcaK cbaK', 3) #subn還能告訴你我替換了幾個
re模塊中compile的方法:
obj=re.compile('\d{3}') #將正則表達式編譯成一個正則表達式對象,就是經過compile方法把\d{3}編譯一下複製給obj,編譯的結果就是正則的規則,上面就是要匹配三個數字 ret=obj.search('abc123cba456bca678') print(ret.group()) ret=obj.findall('abc123cba456bca678') print(ret) #在經過上面已經編譯好的規則調用re的其餘方法,就能夠完成預設定的規則 執行結果: 123 ['123', '456', '678'] #compile的功能在你代碼中須要常常用,並且正則規則長而複雜的時候就能夠用到compile了
re模塊中finditer的方法:
finditer中的iter就是咱們以前學的迭代器,find和iter結合以後就能實現一些功能,以前咱們介紹過iter有一個優點就是節省內存,那麼在須要匹配一個不少內容的狀況下iter就有優點了
ret=re.finditer('\d','abc123bca456cab678') print(ret) #執行結果 #<callable_iterator object at 0x0000029B57391BE0> #能夠看到返回了callable_iterator這個,說明是一個可用的迭代器 print(next(ret).group()) #執行結果 #1 print(next(ret).group()) #要配合使用next方法 #執行結果 #2 print([i.group()for i in ret])#取所有結果就須要用到for #執行結果 #['3', '4', '5', '6', '6', '7', '8'] finditer執行的結果是拿到了迭代器,迭代器裏面的每個元素要調用group才能拿到結果
flags的多選值
re.I(IGNORECASE)忽略大小寫,括號內是完整的寫法 re.M(MULTILINE)多行模式,改變^和$的行爲 re.S(DOTALL)點能夠匹配任意字符,包括換行符 re.L(LOCALE)作本地化識別的匹配,表示特殊字符集 \w, \W, \b, \B, \s, \S 依賴於當前環境,不推薦使用 re.U(UNICODE) 使用\w \W \s \S \d \D使用取決於unicode定義的字符屬性。在python3中默認使用該flag re.X(VERBOSE)冗長模式,該模式下pattern字符串能夠是多行的,忽略空白字符,並能夠添加註釋
ret中的remove的方法:
num1="1,2,4,45.56,12" #注意我要匹配的字符裏面有小數 ret=re.findall(r"\d+",num1) print(ret) # 執行結果: # ['1', '2', '4', '45', '56', '12'] #直接把小數給分開匹配了 num1="1,2,4,45.56,12" #注意我要匹配的字符裏面有小數 ret=re.findall(r"\d+\.\d+|\d+",num1) #在前面先匹配下小數在匹配整數便可,可是匹配的時候.要轉義下,否則就匹配任意字符 print(ret) # 執行結果: # ['1', '2', '4', '45.56', '12']#能夠正常取到小數了 #可是若是是要把小數去掉,只要整數,這個就很差辦了,只能想辦法取出來在去掉 num1="1,2,4,45.56,12" #注意我要匹配的字符裏面有小數 ret=re.findall(r"\d+\.\d+|(\d+)",num1)#我直接只用分組優先的原則,讓匹配的時候先去匹配有小數的, #後面分組優先了,發現及要匹配小數又要匹配其它數字,結果就不顯示了 print(ret) # 執行結果: # ['1', '2', '4', '', '12']#就取到了一個空 print(ret.remove('')) #直接使用remove去除空格就行 print(ret) 執行結果 ['1', '2', '4', '12'] #獲得的就是去掉小數的整數
時間戳:
import time time1=time.time() print(time1) 這個打印出來的就是時間戳格式,以前咱們用於計算程序執行時間的時候用到了, time1=time.time() time2=time.sleep(12) time3=time.time() print(time1-time3) 可是這種格式的時間不便於讀取
時間戳 1541051493.6167455打印出來的是一個不便於讀取的float類型數字,計算的是從1970年00:00:00開始計算的偏移量。
格式化時間字符串:
格式化時間字符串就是正常可讀的時間。
print(time.strftime('%Y:%m:%d %X')) print(time.strftime('%Y:%m:%d %H-%M-%S')) #爲何是格式化時間,首先年月日時分秒是能夠本身選要顯示那些 #還有就是打印的形式也是能夠本身去定義的
%y 兩位數的年份表示(00-99) %Y 四位數的年份表示(000-9999) %m 月份(01-12) %d 月內中的一天(0-31) %H 24小時制小時數(0-23) %I 12小時制小時數(01-12) %M 分鐘數(00=59) %S 秒(00-59) %a 本地簡化星期名稱 %A 本地完整星期名稱 %b 本地簡化的月份名稱 %B 本地完整的月份名稱 %c 本地相應的日期表示和時間表示 %j 年內的一天(001-366) %p 本地A.M.或P.M.的等價符 %U 一年中的星期數(00-53)星期天爲星期的開始 %w 星期(0-6),星期天爲星期的開始 %W 一年中的星期數(00-53)星期一爲星期的開始 %x 本地相應的日期表示 %X 本地相應的時間表示 %Z 當前時區的名稱 %% %號自己
結構化時間:
這個主要表示年,月,日,時,分,秒等,有這樣的時間,咱們就能計算出上一個時間和下一個時間通過了多少年多少月多少天這樣的數值。
time1=time.localtime() print(time1) #執行結果 #time.struct_time(tm_year=2018, tm_mon=11, tm_mday=1, tm_hour=14, tm_min=23, tm_sec=22, tm_wday=3, tm_yday=305, tm_isdst=0) #能夠發現是一個元祖 time1=time1.tm_year,time1.tm_yday print(time1) #執行結果 (2018, 305)#能夠經過這個單獨取時間。
時間戳是給計算機看的,格式話時間字符串是給人看的,結構化是用於計算並更好的表達時間。
上面的三種時間表示方式之間只能經過結構化時間來進行過渡以後相互轉換,格式化時間不能和時間戳直接進行轉換的。
時間戳轉格式化 t1=time.time() print(time.localtime(t1)) print(time.gmtime(t1)) #gmtime是西方時間 #執行結果 #time.struct_time(tm_year=2018, tm_mon=11, tm_mday=1, tm_hour=14, tm_min=38, tm_sec=59, tm_wday=3, tm_yday=305, tm_isdst=0) #time.struct_time(tm_year=2018, tm_mon=11, tm_mday=1, tm_hour=6, tm_min=38, tm_sec=59, tm_wday=3, tm_yday=305, tm_isdst=0)
#格式化時間轉時間戳 print(time.mktime(time.localtime())) #執行結果 1541054663.0
#格式化時間轉結構化時間 print(time.strptime('2018-11-11','%Y-%d-%m'))#給的時間必定是字符串,後面必須告訴要以什麼形式顯示 #執行結果 #time.struct_time(tm_year=2018, tm_mon=11, tm_mday=11, tm_hour=0, tm_min=0, tm_sec=0, tm_wday=6, tm_yday=315, tm_isdst=-1)
#結構化時間轉格式化時間 print(time.strftime('%Y-%d-%m %H-%M-%S',time.localtime(2000000000))) #一樣得提早指定顯示的形式%Y-%d-%m %H-%M-%S #執行結果 2033-18-05 11-33-20
print(time.asctime(time.localtime(2000000000))) #執行結果 #Wed May 18 11:33:20 2033 print(time.asctime()) #執行結果 Thu Nov 1 15:55:18 2018
print(time.ctime()) #執行結果 Thu Nov 1 15:56:25 2018 print(time.ctime(2000000000)) #執行結果 #Wed May 18 11:33:20 2033
formerly_time=time.mktime(time.strptime('2013-09-06 12:12:12','%Y-%m-%d %H:%M:%S')) now_time=time.mktime(time.strptime('2018-11-11 12:12:13','%Y-%m-%d %H:%M:%S')) poor_time=now_time-formerly_time The_current_time=time.gmtime(poor_time) print('過去了%d年%d月%d天%d小時%d分鐘%d秒'%(The_current_time.tm_year-1970,The_current_time.tm_mon-1,The_current_time.tm_mday-1,The_current_time.tm_hour,The_current_time.tm_min,The_current_time.tm_sec)) 執行結果: 過去了5年2月7天0小時0分鐘1秒
返回隨機數能夠用到的場景很少,可是好比我要必定區間裏面抽取一些數據,這個就有點像抽獎,或者是驗證碼這種的就可使用隨機數模塊。
import random print(random.random()) #執行結果 0.06421289957677301 #會隨機返回一個大於0小於1的小數 print(random.uniform(1,5)) #執行結果 3.069081294864049 #會在你指定的1到5的區間內返回一個小數
print(random.randint(1,6)) #執行結果 2 6 #返回一個1到6的整數,小於1和等於6的整數 print(random.randrange(1,6,2)) #執行結果 1#返回一個大於1小於6區間內的奇數,並且這個2是步長
#print(random.choice([1,19,68,[99,102]])) #執行結果 68#能夠接收一個列表,或者說是可迭代的,在指定的範圍隨機抽取 print(random.sample([1,12,66,68,32,[99,102]],3)) #執行結果 [1, 68, [99, 102]] #從列表或者可迭代範圍內隨機抽取指定的數的數據,3就表明指定抽取3個數
num=[2,4,6,8,10,12] random.shuffle(num) print(num) #執行結果 [12, 10, 4, 2, 6, 8]#對指定的數據隨機打亂排序 [8, 12, 2, 4, 10, 6]
if 1>0: kk1 = "" for i in range(6): num=random.randint(0,9) letter=chr(random.randint(65,90)) add=random.choice([num,letter]) kk1="".join([kk1,str(add)]) print(kk1) else: print("生成驗證碼失敗")
os模塊這個以前咱們也用到了不少,os發生交互的主要是安裝python的操做系統,屬於進場使用的模塊之一,本篇不在作詳細的講解,具體的用法能夠參照下面網址來進行查詢。
http://www.runoob.com/python3/python3-os-file-methods.html
import sys print(sys.platform) 執行結果 win32 這個方法執行的記過不少狀況下不許,若是是非要查看系統版本,建議不要使用這個模塊,不過它能夠用來查詢linux系統上是哪一個分支的linux
print(sys.version) 執行結果: 3.6.5rc1 (v3.6.5rc1:f03c5148cf, Mar 14 2018, 03:12:11) [MSC v.1913 64 bit (AMD64)] 能夠查詢到python解釋器的版本
sys.exit()
用來退出程序,這個會用在當程序出現了異常,不能正常退出了,就能夠在程序裏面寫一個exit,告訴系統程序退出,正常的狀況下退出返回給系統的是0,錯誤的狀況下返回給系統的就是1
print(sys.path) 返回模塊的路徑,當咱們import某個模塊的時候,就是調用的sys.path去找到模塊所在的路徑,若是是你本身編寫的一些模塊或者方法要頻繁使用的話就可使用sys.path來指定
argv1=sys.argv name=argv1[1] age=argv1[2] if name=='kk' and age=='22': print("符合年齡") sys.exit() else: print('年齡不符合')
咱們能夠經過定義argv事先指定好一些固定的指,讓腳本執行的時候攜帶這些參數,從而去判斷腳本執行的狀況:
首先咱們要理解什麼是序列,在python裏面序列一般指的就是字符串,那麼咱們理解序列化就簡單了,就是將其它數據類型轉換成一個字符串類型就是指的序列化。
爲何要轉換成字符串類型,其實意義很明顯,由於其它的數據類型不方便讀,假如你寫了一個爬蟲爬去了一些數據,結果表現的形式是字典,列表等形式,不明白的人不清楚你這個是什麼內容,爲了方便普通大衆能有好的讀取的程序的結果,轉化字符串是必要的。
還有就是網絡傳輸的過程當中用的形式是byte,也是須要將數據轉換成字符串的。
最後就是寫了一個程序,有時候就是須要把其它數據類型轉換成字符串的或者把字符串轉換成其它數據類型的時候,也是須要用到序列化的,固然從其它轉換成字符串的也叫反序列化。
json是一種通用的序列化格式,不止在python,包括其它語言也能使用,例如我經過python程序獲取了一部分數據,可是這部分數據須要提供給java編寫的程序使用,那麼json就能很好的把數據轉換,給下面的節點程序提供數據來源。
json不是說萬能的,必定有在python裏面的數據類型,是不能被轉換的,因此咱們在寫程序的時候,必定要儘可能少用多種數據類型,避免出現數據不能轉換的時候問題
pickle這個彌補json不能轉換全部python數據類型的缺點,pickle能解決python裏面全部的數據類型序列化,可是缺點就是pickle出來的數據類型只有python才能理解,不通用,例如我在本地寫了一個程序,這個程序調用了我本地安裝的一個python外部模塊(本身編寫的模塊),而後把這個程序給別人用的時候,別人本地也必須安裝這個外部模塊,否則我程序裏面pickle的數據是不能被python正常識識別的。因此pickle只能用於python。
shelve是python3裏面新出現的一種新序列化方法,它能序列化成一個句柄,而後對這個序列化句柄直接操做,相比前面兩個要簡單方便。
json模塊的四種功能:
import json dic={'k1':22,'k2':'pyhton'} print(type(dic),dic) dic1=json.dumps(dic) print(type(dic1),dic1) 執行結果: <class 'dict'> {'k1': 22, 'k2': 'pyhton'} <class 'str'> {"k1": 22, "k2": "pyhton"} #能夠看到類型變成了str類型,並且顯示都是雙引號顯示的 在json裏面對數據格式有嚴格的要求,這個要注意一下。
import json dic={'k1':22,'k2':'pyhton'} print(type(dic),dic) dic1=json.dumps(dic) print(type(dic1),dic1) dic2=json.loads(dic1) print(type(dic2),dic2) #執行結果 <class 'dict'> {'k1': 22, 'k2': 'pyhton'} <class 'str'> {"k1": 22, "k2": "pyhton"} <class 'dict'> {'k1': 22, 'k2': 'pyhton'}#能夠看到數據被反序列化了。變回dict類型了
在python能被正常序列化的數據就數字,字符串,字典,列表
元組如今也能被序列化,可是序列化以後會變成列表,這個須要注意下。
import json dic=(123,456,789) print(type(dic),dic) dic1=json.dumps(dic) print(type(dic1),dic1) dic2=json.loads(dic1) print(type(dic2),dic2) #執行結果 <class 'tuple'> (123, 456, 789) <class 'str'> [123, 456, 789] #能夠看到變成了列表 <class 'list'> [123, 456, 789] #反序列化也仍是列表list,並不能反序列化成元組
dumps和loads是對內存裏面的數據進行操做,操做以後的數據仍是在內存當中。
dump和load是對文件進行操做
import json dic1={'a':1,'b':2} file=open('kk1.log','w',encoding='utf8') json.dump(dic1,file) file.close() #執行結果: kk1.log文件裏面的內容{"a": 1, "b": 2}
import json file=open('kk1.log',encoding='utf8') dic1=json.load(file) print(type(dic1),dic1) file.close() #執行結果: <class 'dict'> {'a': 1, 'b': 2}
import json dic1={'a':1,'b':'呵呵'} file=open('kk1.log','w',encoding='utf8') json.dump(dic1,file) file.close() #執行結果 {"a": 1, "b": "\u5475\u5475"}#發現寫到文件裏面的中文,我已經制定編碼格式了,可是寫進去的仍是byte類型。 file=open('kk1.log',encoding='utf8') dic1=json.load(file) print(type(dic1),dic1) file.close() #執行結果 <class 'dict'> {'a': 1, 'b': '呵呵'}發現序列化的時候沒有問題。 import json dic1={'a':1,'b':'呵呵'} file=open('kk1.log','w',encoding='utf8') json.dump(dic1,file,ensure_ascii=False) file.close() #執行結果 {"a": 1, "b": "呵呵"}#只要使用ensure_ascii=False便可,不影響你讀取
import json dic1={'a':1,'b':'呵呵'} file=open('kk1.log','w',encoding='utf8') json.dump(dic1,file,ensure_ascii=False) json.dump(dic1,file,ensure_ascii=False)#我分別寫兩次數據 file.close() #執行結果 {"a": 1, "b": "呵呵"}{"a": 1, "b": "呵呵"}#文件裏面的內容 file=open('kk1.log',encoding='utf8') dic1=json.load(file) print(type(dic1),dic1) file.close() #執行結果 json.decoder.JSONDecodeError: Extra data: line 1 column 20 (char 19) #發現讀取的時候報錯了 這個是由於dump的數據只能load一次,要麼一次性把數據dump進去,在進行load讀取出來。
l1=[{'k1':'111'},{'k2':'111'},{'k3':'111'}] file_log=open('file1','w') for dic in l1: str_file=json.dumps(dic) file_log.write(str_file+'\n') #經過循環dumps數據到flie1文件裏面,每循環寫一次就換行 file_log.close() # #執行結果 # {"k1": "111"} # {"k2": "111"} # {"k3": "111"} file_log=open('file1') l1=[] for str_file in file_log: dic_file=json.loads(str_file.strip())#在此處必定要加strip去掉換行 l1.append(dic_file) file_log.close() print(type(l1),l1) #執行結果 <class 'list'> [{'k1': '111'}, {'k2': '111'}, {'k3': '111'}] #能夠看到取出來仍是和dumps進去的格式同樣 解決上面dump分批寫,不能讀的問題,關鍵點就在於我每次dumps的時候換行,這樣我在讀的時候也是一行一行的loads
pickle的用法和json的方法同樣,也是dumps,loads,dump,load四中方法,不過pickle就能直接避免json.load讀數據的問題,還有就是pickle對文件進行操做的時候都須要加上b。
import pickle l1=[{'k1':'111'},{'k2':'111'},{'k3':'111'}] l2=[{'k1':'111'},{'k2':'111'},{'k3':'111'}] file_log=open('file1','wb') #pickle須要注意dump的時候須要wb的形式 pickle.dump(l1,file_log) pickle.dump(l2,file_log) file_log.close() file_log1=open('file1','rb') #load的時候須要rb的形式 pickle_file1=pickle.load(file_log1) pickle_file2=pickle.load(file_log1) print(pickle_file1) print(pickle_file2) file_log1.close() 執行結果 [{'k1': '111'}, {'k2': '111'}, {'k3': '111'}] #pickle就能避免json.load讀數據的問題 [{'k1': '111'}, {'k2': '111'}, {'k3': '111'}]
shelve就區別於上面的json和pickle,它就一種用法就是open,shelve open一個文件以後就會得到一個文件句柄,而後咱們能夠把須要寫的數據類型往句柄裏面寫。
import shelve file_log=shelve.open("shelve_file") file_log['key']={'int':10,'float':78.2,'string':"kkk"} file_log.close() #執行結果 本地會產生三個文件,shelve_file.bak結尾,shelve_file.dat結尾,shelve_file.dir結尾,這三個文件打開以後發現 內容是讀不明白的,可是這三個文件一個都不能少。 file_log1=shelve.open('shelve_file') kk=file_log1['key'] file_log1.close() print(kk) #執行結果 {'int': 10, 'float': 78.2, 'string': 'kkk'}
shelve在官網有作說明,shelve不容許同一時間進行讀寫操做,因此在寫代碼的時候必定要看下場景,若是都是讀操做,就可使用shelve,shelve在只讀模式下有一個問題,就是隻讀的模式還能進行寫操做,這個得注意下。
import shelve file_log=shelve.open('shelve_file') print(file_log['key']) file_log['key']['nuw_key']='12kkkkk' #在此處我新加了一段字符串 file_log.close() file_log=shelve.open('shelve_file') print(file_log['key']) 執行結果 {'int': 10, 'float': 78.2, 'string': 'kkk'} {'int': 10, 'float': 78.2, 'string': 'kkk'} #結果發現新加的數據並無被寫進去 file_log1=shelve.open('shelve_file',writeback=True) #上面的問題指須要加上writeback=True就能夠 writeback參數只要記錄在open文件的後面就能記住全部的增刪改的操做 print(file_log1['key']) file_log1['key']['nuw_key']='12kkkkk' file_log1.close() 執行結果 {'int': 10, 'float': 78.2, 'string': 'kkk', 'nuw_key': '12kkkkk'}#能夠看到數據被加進去了
writeback也是有點明顯,就是能讓用戶操做持久化,可是缺點也是明顯的,就是writeback會在每次使用的時候消耗必定內存,並且每次writeback都是一次從新的操做,不管你前面有多少數據,都會從新操做一次,這樣也會增長沒必要要的時間消耗。
在不少狀況下咱們寫的基礎功能代碼都是能夠複用,例如咱們上面寫的生成驗證碼的代碼,這個時候我在寫各類各樣的登陸相關的代碼確定會用到驗證碼的功能,那麼咱們是否是應該把驗證碼功能製做成一個模塊,後面直接一句import 把驗證碼模塊導入就可使用了。
能夠看到我寫了一個module的python文件,在另一個模塊文件裏面import就能將事先寫好功能的文件給調用執行,這個就是上面說的模塊導入複用,固然import的時候必須指定要導入模塊的名字,模塊的名字要符合python文件的命名規則。
仍是這個文件,雖然能被複用,可是咱們在上面學習其它模塊的時候,發現模塊都都是time.time這樣的,咱們導入模塊用了模塊提供的一些方法,並非說和上面同樣很簡單的功能。
咱們在模塊裏面在加上一個函數,這個函數就至關於模塊裏面的方法,咱們在調用模塊在加方法就能直接運行函數獲得結果。
那麼import是怎麼把我寫的module給調用執行的了,首先,import module的時候,import會去路徑下找文件名相符的文件,而後把文件裏面的內容一行一行的讀取到內存中(專屬的命名空間),這個專屬命名就是module,專屬命名空間就是以module命名的內存空間,最後咱們要去執行module.kk的時候就是執行了事先讀取到module命名空間裏面的代碼。
還有就是這個module命名空間裏面的代碼即便和當前調用module文件裏面的代碼有重名的,也不會受到影響,由於module這個空間是和其它內存空間是隔離的,不會因其它空間有相同的名字受到影響。
我在一個文件import了module三次,結果發現執行的時候就執行了一次,這個是由於咱們在import的時候python會去執行python解釋器交互模塊sys,而後使用sys裏面的sys.modules.keys()方法,這個方法會去檢查我導入的module模塊是否是已經存在了,存在了就不會重複導入了,若是module不在sys.modules.keys()方法檢查檢查的結果裏面,就會用到sys.path方法去找路徑這個模塊對應的文件。
在後面import一個本身寫的模塊的時候,發現導入不成功,解決的思路就應該看看sys.modules.keys()和sys.path出來的結果。
給模塊起別名的方法很簡單,只要在導入模塊以後加上一個as,後面在指定別名便可。
import module as mm mm.kk() #執行結果 這是一個模塊 這仍是一個模塊 經過導入模塊的時候後面加上as,再加上給模塊起的別名,就能完成正常模塊全部功能
爲何要給模塊起別名,假如我寫了兩個模塊,兩個模塊都是打開文件,可是一個是專門處理jpg格式的文件,一個是處理png格式的文件,那麼這兩個模塊的基礎部分代碼都是相同的,都是要取open文件,以後我在其它代碼裏面import這兩個模塊的時候,發現我處理的文件夾下面既有jpg格式的文件又有png格式的文件,我就須要在代碼裏面寫jpg.open_flie和png.open_file方法,這樣就會出現我能不能只關心open_file文件就行,不須要每次都去指定我是要那種格式的方法,這個時候別名就能完成這種事情,我在代碼裏面加上判斷,無論是那種格式的文件均可以用一個方法給打開。
word='import word' if word==word: import word as file else: import execl as file file.open_file() #執行結果 #這是一個word模塊 if word!=word: import word as file else: import execl as file file.open_file() # 執行結果: # 這是一個execl模塊
上面只是一種常見的場景介紹,實際狀況下,你可能須要和不少文件打交道,別名就能很好的簡化你的代碼,至關於作了一個兼容的功能,固然記得對你的代碼加註釋,保證其餘人也能看懂。
注意,只要給模塊起了別名,模塊本來的名字就不可用了。
import time import sys import random 在正常狀況下咱們導入模塊都是一個個導入的 import time,sys,random 其實也能夠逗號把模塊分開,在一行就能導入
模塊能在一行同時導入,可是不建議寫一行,入股是導入了20個模塊,你都寫在一行,這樣後續代碼改起來必然會增長工做量,因此在一行導入模塊的時候要保證模塊少,且模塊都屬於一個類型的,這裏的類型是指內置模塊和擴展模塊。在導入模塊的時候,也是有規則的,就是先導入內置的,在導入擴展的,最後纔是自定義。
from time import sleep sleep(1) 使用from的模塊導入方法就能直接使用到模塊裏面的方法
使用from以後導入的模塊方法,本地的代碼是不能出現和模塊方法同名的代碼塊方法,這樣你導入的模塊的方法是不會生效的。
當一個模塊方法不少,我也要用到這個裏面的不少方法,就可使用from 模塊 import * ,這樣就能夠一下導入全部的方法。
from time import * sleep=10 sleep(1) 執行結果 TypeError: 'int' object is not callable 會發現執行報錯了
from 模塊 import *當然好用,可是不太安全,當出現變量名和方法名一致的,模塊方法就會失靈了,並且一下導入全部方法,若是是用不上,也會佔用一些沒必要要的空間,因此是用到哪些方法就import哪些方法。
可是有時候咱們寫代碼,不會有那麼多時間去規劃我代碼裏面要用到模塊裏面的那些方法,這個時候import *顯然更加便捷,但同時咱們又說了import *方法不安全,能不能有一種方法先能夠import *進來,而後我在邊寫邊指定用到那些方法,下面咱們說到就是__all__方法。
__all__=['time'] #下面strptime方法報錯是由於__all__方法致使的,__all__=[]格式,必須是__all__=後面是列表, # 列表裏面必須是字符串,這個字符串必須是以存在的方法名 from time import * print(time()) #執行結果 1541558971.905012 #執行結果 #TypeError: _strptime_time() missing 1 required positional argument: 'data_string' #strptime是屬於time模塊的,上面也import *號,可是仍是有報錯
這樣咱們就能夠經過__all__每次去指定我要使用模塊的那些方法,可是要記住__all__只能對import *後面這個*號作約束。
在實際的開發場景中,多人協同開發一個功能的時候,都會定下一些共用的基礎代碼,這些代碼會被不一樣的人全部使用,那麼天然而然你的代碼就須要迭代優化,那麼最好的狀況下就是不中斷同事的開發進度的基礎上進行代碼的優化,那麼這個就須要使用代碼隔離操做。
def open_file(): print('這是一個word模塊') print(__name__) #執行結果 __main__#這個結果必須是在有__name__的代碼裏面執行才能獲得main,這樣咱們就能夠控制其它代碼調用本函數的時候是否能正常執行 if(__name__) != '__main__': def open_file(): #若是name=main了就執行函數 print('這是一個word模塊') else: #不然我就執行其它的內容 print('log')
經過__name__就能夠把一些事先準備好的函數給隔離開,避免其它代碼import的時候出現問題調試的時候代碼會被其它代碼給執行掉。
什麼是包,包就是把一堆模塊放在一塊兒的集合就叫作包,能夠想象包就是一個工具箱,只要拿到這個工具箱,就能夠完成相應的事情。
咱們進入python的安裝目錄下到lib目錄下咱們就能夠看到以前咱們學習過的不少模塊名字的文件夾,其實包在pyhton裏面的含義就是吧解決通一類問題的模塊放在一個文件夾下面,那麼這個文件夾就是包。
這個文件夾裏面的文件咱們經過一個個打開以後發現一個相同的文件就是__init__.py的文件,在python裏面只有含有__init__.py文件才能算作包。並且這邊__init__文件在python2裏面是必須有的,否則就會有問題,但在python3裏面若是沒有也是能夠用的。
在包的導入方法裏面必須遵循:凡是導入帶點的,點的左邊必須是一個包,並且若是是多層級的包,須要每層遞歸。
import txt.api.abc as api api.get() #txt是最外面的包 #api是txt裏面的包 #abc是代碼文件 #get是代碼裏面方法 #abc.py裏面的代碼 #def get(): # print('from abc.py') #執行結果 from abc.py
導入以後就沒有限制了,點的左邊能夠是包,函數,模塊,類等,可是得知足上的條件。
from txt.api import abc abc.get() #from txt import api.abc 錯誤的from方法,前面是from後面是import,後面這個import就不能再帶點 from txt.api.abc import get get() #txt是最外面的包 #api是txt裏面的包 #abc是代碼文件 #get是代碼裏面方法 #abc.py裏面的代碼 #def get(): # print('from abc.py') #執行結果 from abc.py from abc.py
經過上面的例子,咱們也能夠看到,不管是import仍是from,你必須先從最外的一層目錄開始,不能直接跳過最外面的目錄import或者from第二次目錄的,這個其實就和以前說的sys.path有關,你在import的時候都是先去看目錄下有沒有這個文件,從最簡單的理論上來講,文件要執行必須從1到2再到3,不能直接從2到3,若是你非要import和from第二層目錄,能夠在sys.path裏面指定好。
仍是上面路徑的問題,包的根本執行方式其實仍是最簡單的路徑去檢索方法而已,咱們上面執行都是在一行執行import xxx.xxx form xxx這樣的形勢,若是是換了目錄執行方式是import就導入包的第一層目錄,這個時候確定會報錯,因此咱們要對代碼進行優化,保證兼容性性問題。
例如上圖的結構,我在外面導入包的最外層目錄,在第二行包裏面的方法,這個時候咱們須要在每一層的init文件裏面import包裏面下一層目錄,一層層遞歸的import。
xml文件下的init文件 from xml import txt txt文件下的init文件 from xml.txt import api api文件下的init文件 from xml.txt.api import abc 經過每一層目錄的導入,層層遞歸 import xml xml.txt.api.abc.get() 執行結果 from abc.py 在外面import的時候就不會有報錯了,兼容性更強
咱們在使用linux的時候會常用cd ..返回上一層目錄的操做,在python裏面其實也是同樣的,一個點表明當前目錄,兩個點表明上一層目錄。
xml文件下的init文件 from . import txt txt文件下的init文件 from . import api api文件下的init文件 from . import abc 咱們把以前須要層層寫明的目錄結構改爲點,也是能夠執行的。 import xml xml.txt.api.abc.get() 執行結果 from abc.py 在外面import的時候就不會有報錯了,兼容性更強
這個優化還有一個好處,就是當個人xml目錄調整路徑了,我也不用擔憂我xml裏面沒一個init須要隨之改變目錄結構,我只須要在更改的目錄下加上新路徑就好了,例如from call import xml,等於說不管我xml整個路徑放在哪一個路徑下,我都不用關心xml裏面的調用,我只須要關心外面的調用路徑是否正確,固然這個調整也存在一個弊端,若是你在xml裏面調用xml路徑下的方法就有問題了。
在版本2咱們看見了可使用點表明目錄,其實咱們以前學到的__all__方法也是能夠在包裏面使用的,固然仍是隻能控制__all__是用於控制from...import *
因此絕對路徑和相對路徑都各自有利弊,絕對路徑目錄結構清晰,可是不能挪動目錄所在位置,相對路徑能挪動,可是不能包裏面調用相同層級下其它的方法。
包在編寫的時候要記住不能相互依賴,例如你寫了兩個包之間相互依賴這種是不行的,可是你能夠在包裏面依賴python自帶的模塊。