目錄:
一、協程函數
表達式形式的yield的用途
二、函數的遞歸調用
三、匿名函數lambda
max()
min()
sorted()
map()
reduce()
filter()
四、模塊
1.什么是模塊
2. 為何要使用模塊
3. 導入模塊
3.1 import
3.2 from … import …
3.3 把模塊當做腳本執行
3.4 模塊搜索路徑
3.5 dir()函數
五、包
5.1 import
5.2 from … import …
5.3 __init__.py文件
5.4 from glance.api import *
5.5 絕對導入和相對導入
5.6 單獨導入包
六、re模塊
re模塊提供的方法
上一節課生成器還有一些知識點沒講到,接下來補充;
一、協程函數
生成器:yield關鍵字的另外一種用法
yield的語句形式有兩種:
- yield n //n為yield的返回值
- x = yield n //yield的表達式形式,n為yield返回值
現在模擬去餐廳吃飯,服務員上一盤菜,你就吃一盤,然后等著服務員再繼續上菜
def eater(name):
print('%s ready eat something ...'%name)
food_list = []
while True:
food = yield food_list
print('%s eat a %s'%(name,food))
food_list.append(food)
g=eater('alex') # g是一個生成器對象
next(g) # 第一次必須給yield傳一個空值
res = g.send('餃子') #g.send('餃子')就相當于服務員上菜
print(res)
g.send('包子')
res1 = g.send('餅子')
print(res1)
輸出結果:
alex ready eat something ...
alex eat a 餃子
['餃子']
alex eat a 包子
alex eat a 餅子
['餃子', '包子', '餅子']
執行流程解析:
- g=eater('alex') ,g是一個生成器對象,這一步并不會使eater()函數執行;
- 遇到next(g),會觸發eater('alex')開始執行;
- eater('alex')執行中遇到yield關鍵字時,函數執行暫停在此處,并返回yield后面的值food_list(此時food_list是一個空列表),跳出到函數外面觸發eater('alex')執行的一行(next(g)所在的那一行);
- next(g)接收yield返回的food_list(此時是空列表),并繼續往下執行;
- 當遇到g.send('餃子')時,會將'餃子’傳給yield,并觸發eater('alex')從上次暫停的位置(yield處)開始執行,yield接收g.send('餃子')傳進來的值,并賦值給food (此時food = '餃子'),然后eater('alex')函數繼續往下執行;
- while True死循環,循環到下一次yield時,暫停執行,將food_list返回給g.send('餃子'),此時的food_list加入了food(food='餃子'),并將food_list賦值給res;
- 繼續往下執行,每當遇到g.send()就會重復第5步的流程,遇到yield就會重復第6步的流程;
總結:
-
next(g)
和g.send('x')
都能觸發生成器函數的執行,不同的是send可以給yiled傳值; - 第一次給
yield
傳值,必須傳空值,next(g)
也相當于給yield傳了一個空值,所以next(g)
也可用g.send(None)
替換; - 由于給每個生成器函數傳值之前必須先傳一個空值,這樣很容易讓人忘掉此步驟,基于此,可以將此步驟寫成一個裝飾器去實現;這樣就可以在多處使用了;請看下面的實現代碼 。
應用如下:
def init(func):
'''這個裝飾器的作用是初始化其他生成器函數'''
def wrapper(*args,**kwargs):
g=func(*args,**kwargs)
next(g)
return g
return wrapper
init # eater = init(eater)
def eater(name):
print('%s ready eat something ...'%name)
food_list = []
while True:
food = yield food_list
print('%s eat a %s'%(name,food))
food_list.append(food)
g=eater('alex')
# res=next(g)
g.send('餃子')
g.send('包子')
g.send('餅子')
print(g.send('牛奶'))
輸出結果:
alex ready eat something ...
alex eat a 餃子
alex eat a 包子
alex eat a 餅子
alex eat a 牛奶
['餃子', '包子', '餅子', '牛奶']
可以看出,注釋掉res = next(g)
后,執行結果與之前無異。
表達式形式的yield的用途
# 實現類似grep -rl ‘xxx’ /root 的功能;
# 實現的功能是查找出root目錄下所有包含xxx的文件,并將文件名返回;
import os
def init(func):
'''這個裝飾器的作用:初始化一個生成器'''
def wrapper(*args,**kwargs):
res = func(*args,**kwargs)
next(res)
return res
return wrapper
init
def ergodic(target):
'''提取傳入的目錄下所有文件的絕對路徑,發送給opener()'''
while True:
path = yield
file_list = os.walk(path)
for par_dir,_,files in file_list:
for file in files:
file_abs_path = r'%s\%s'%(par_dir,file)
target.send(file_abs_path)
init
def opener(target):
'''接收ergodic()傳遞的文件絕對路徑,打開,并讀取每行一內容,傳遞給grep()'''
while True:
file_abs_path = yield
with open(file_abs_path,encoding='utf-8') as f:
for line in f:
tag = target.send((file_abs_path,line))
if tag:
break
@init
def grep(target,pattern):
'''
接收opener()傳遞的每一行,判斷pattern(需要搜索的關鍵字)是否在行中,\
然后將文件絕對路徑傳給printer()
'''
tag = False
while True:
file_abs_path,line = yield tag
tag = False
if pattern in line:
tag = True
target.send(file_abs_path)
@init
def printer():
'''打印文件絕對路徑'''
while True:
file_abs_path = yield
print(file_abs_path)
gen = ergodic(opener(grep(printer(),'python')))
path = r'E:\python\s17\day05\練習\a'
gen.send(path)
執行結果:
E:\python\s17\day05\練習\a\a.txt
E:\python\s17\day05\練習\a\b\b.txt
E:\python\s17\day05\練習\a\b\c\d\d.txt
E:\python\s17\day05\練習\a\b1\b.txt
二、函數的遞歸調用
在函數調用過程中,直接或間接地調用了函數本身, 這就是函數的遞歸調用,如下示例:
def f1():
print('from f1')
f1()
f1()
注:上述代碼,f1()的函數體內又調用了它本身,當在外部調用f1()時,會產生一個死循環(python默認遞歸調用設定為1000次,可以通過下述方法獲取);
import sys
print(sys.getrecursionlimit()) #獲取python遞歸調用限制次數
sys.setrecursionlimit(100000) #設定遞歸調用限制次數
print(sys.getrecursionlimit())
# 輸出結果:
1000
100000
遞歸的特性:
- 必須有一個明確的結束條件;
- 每次進入更深一層遞歸時,問題規模相比上次遞歸都應該有所減少;
- 遞歸效率不高,遞歸層次過多會導致棧溢出(在計算機中,函數調用是通過棧(stack)這種數據結構實現的,每當進入一函數調用,棧就會加一層棧幀,每當函數返回,棧就會減一層幀。由于棧的大小不是無限的,所以遞歸調用的次數過多,會導致棧溢出)
堆棧掃盲,請點擊查看
遞歸函數實際應用,二分查找:
l = [1, 2, 10,33,53,71,73,75,77,85,101,201,202,999,11111]
def search(find_num,seq):
if len(seq) == 0:
print('not exists')
return
mid_index=len(seq)//2
mid_num=seq[mid_index]
print(seq,mid_num)
if find_num > mid_num:
#in the right
seq=seq[mid_index+1:]
search(find_num,seq)
elif find_num < mid_num:
#in the left
seq=seq[:mid_index]
search(find_num,seq)
else:
print('found it')
search(77,l)
search(72,l) # 72不存在l數字列表中
輸出結果:
[1, 2, 10, 33, 53, 71, 73, 75, 77, 85, 101, 201, 202, 999, 11111] 75
[77, 85, 101, 201, 202, 999, 11111] 201
[77, 85, 101] 85
[77] 77
您要查找的數字: 77
found it
[1, 2, 10, 33, 53, 71, 73, 75, 77, 85, 101, 201, 202, 999, 11111] 75
[1, 2, 10, 33, 53, 71, 73] 33
[53, 71, 73] 71
[73] 73
not exists
三、匿名函數lambda
匿名函數就是不需要顯式的指定函數
python 使用 lambda 來創建匿名函數。
- lambda只是一個表達式,函數體比def簡單很多。
- lambda的主體是一個表達式,而不是一個代碼塊。僅僅能lambda表達式中封裝有限的邏輯進去。
- lambda函數擁有自己的命名空間,且不能訪問自有參數列表之外或全局命名空間里的參數。
- 雖然lambda函數看起來只能寫一行,卻不等同于C或C++的內聯函數,后者的目的是調用小函數時不占用棧內存從而增加運行效率。
語法
lambda函數的語法只包含一個語句,如下:
lambda [arg1 [,arg2,.....argn]]:expression
如下實例:
#!/usr/bin/python
# -*- coding: UTF-8 -*-
# 可寫函數說明
sum = lambda arg1, arg2: arg1 + arg2;
# 調用sum函數
print("相加后的值為 : ", sum( 10, 20 ))
print("相加后的值為 : ", sum( 20, 20 ))
# 以上實例輸出結果:
相加后的值為 : 30
相加后的值為 : 40
def calc(n):
return n**n
print(calc(3)) # 結果27
# 換成匿名函數
calc1=lambda x:x**x
print(cal1c(3)) # 結果27
lambda 會將:冒號后面的表達式結果返回
這么用,感覺好像匿名函數也沒有什么用。
不過匿名函數主要是和其它函數搭配使用的。
如下,將匿名函數和內置函數max()、min()、sorted()、zip()。。。結合起來使用
max()
max()函數的功能是獲取序列最大值
以下例子用max()函數獲取工資最高的人名
salaries={
'egon':3000,
'alex':100000000,
'wupeiqi':10000,
'yuanhao':2000
}
def func(k):
return salaries[k]
res = max(salaries,key=func) #1
print(res)
# 輸出結果:
alex
#1 max()函數會遍歷字典salaries中的key,將key當作參數傳給函數func() ,然后函數func()返回key對應的value然后賦值給max()括號中的key,并根據這個key進行比較,然后返回工資最高的人名
將以上代碼通過匿名函數簡化:
salaries={
'egon':3000,
'alex':100000000,
'wupeiqi':10000,
'yuanhao':2000
}
res = max(salaries,key=lambda k:salaries[k])
print(res)
# 輸出結果:
alex
min()
獲取序列最小值,用法和max()函數相同
salaries={
'egon':3000,
'alex':100000000,
'wupeiqi':10000,
'yuanhao':2000
}
res = min(salaries,key=lambda x:salaries[x])
print(res)
# 輸出結果:
yuanhao
sorted()
序列化數據排序,默認是從小到大排序,加reverse=True
關鍵字可以從大到小排序,用法也和max()類似
以下示例,輸出工資從低到高的名字
salaries={
'egon':3000,
'alex':100000000,
'wupeiqi':10000,
'yuanhao':2000
}
print(sorted(salaries,key=lambda x:salaries[x]))
# 輸出結果:
['yuanhao', 'egon', 'wupeiqi', 'alex']
reverse=True
反向排序(從大到小):
salaries={
'egon':3000,
'alex':100000000,
'wupeiqi':10000,
'yuanhao':2000
}
print(sorted(salaries,key=lambda x:salaries[x],reverse=True))
# 輸出結果:
['alex', 'wupeiqi', 'egon', 'yuanhao']
map()
map()
函數接收兩個參數,一個是函數,一個是iterable
(可迭代對象),map
將傳入的函數依次作用到序列的每個元素,并把結果作為新的iterable
返回。
以下示例 ,將不符合規范的名字變成符合規范的首字母大寫的名字
name_li = ['adam','LISA','barI']
print(list(map(lambda x:x.capitalize(),name_li)))
# 輸出結果:
['Adam', 'Lisa', 'Bari']
將每個名字末尾加上指定字符
name_list = ['alex','wupeiqi','yuanhao']
print(list(map(lambda x:x+'_god',name_list)))
# 輸出結果:
['alex_god', 'wupeiqi_god', 'yuanhao_god']
reduce()
python2中reduce()
還是一個單獨的內置函數;python3中則將reduce()
集成到functools
包中了,使用之前需要先導入from functools import reduce
reduce
把一個函數作用在一個序列[x1, x2, x3, ...]
上,這個函數必須接收兩個參數,reduce
把結果繼續和序列的下一個元素做累積計算,其效果就是:
reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)
# f假設是一個函數
如下示例,對一個序列求和
from functools import reduce
num_li = [1,2,3,4,5]
res = reduce(lambda x,y:x+y,num_li)
print(res)
# 輸出結果:
15
給reduce
一個初始值:
from functools import reduce
num_li = [1,2,3,4,5]
res = reduce(lambda x,y:x+y,num_li,10)
print(res)
# 輸出結果:
25
map()
與reduce()
結合使用
將字典中的value取出并組合
def fn(x, y):
return x * 10 + y
def char2num(s):
return {'1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9}[s]
print(reduce(fn, map(char2num, '13579')))
# 輸出結果:
13579
filter()
filter
函數用于過濾序列
和map()
類似,filter()
也接收一個函數和一個序列。和map()
不同的是,filter()
把傳入的函數依次作用于每個元素,然后根據返回值是True
還是False
決定保留還是丟棄該元素。
過慮以‘god’結尾的名字
name_li = ['alex_god', 'wupeiqi_god', 'yuanhao_god','egon']
res = filter(lambda x:x.endswith('god'),name_li)
# res 得到的是一個filter對象
# <filter object at 0x000...748>
print(list(res))
# 輸出結果:
['alex_god', 'wupeiqi_god', 'yuanhao_god']
注意:
map()
和filter()
函數返回的是一個Iterator
,也就是一個惰性序列,所以要強迫map()
和filter()
完成計算結果,需要用list()
函數獲得所有結果并返回list。
四、模塊
1.什么是模塊
一個模塊就是一個包含了python定義和聲明的文件,文件名就是模塊名字加上.py
的后綴
2. 為何要使用模塊
如果你退出python解釋器然后重新進入,那么你之前定義的函數或者變量都將丟失,因此我們通常將程序寫到文件中以便永久保存下來,需要時就通過python test.py方式去執行,此時test.py被稱為腳本script。
隨著程序的發展,功能越來越多,為了方便管理,我們通常將程序分成一個個的文件,這樣做程序的結構更清晰,方便管理。這時我們不僅僅可以把這些文件當做腳本去執行,還可以把他們當做模塊來導入到其他的模塊中,實現了功能的重復利用。
3. 導入模塊
有import ...
和from ... import ...
兩種
3.1 import
示例文件:smap.py, 文件名是spam.py , 模塊名是spam
#spam.py
print('from the spam.py')
money=1000
def read1():
print('spam->read1->money',money)
def read2():
print('spam->read2 calling read')
read1()
def change():
global money
money=0
模塊可以包含可執行的語句和函數的定義,這些語句的目的是初始化模塊,它們只在模塊名第一次遇到導入import語句時才執行,(import語句可以在程序中的任意位置使用,且針對同一個模塊可以import多次,為了防止重復導入,python的優化手段是:第一次導入后就將模塊名加載到內存中,后續的import語句僅是對已經加載到內存中的模塊對象增加了一次引用,不會重新執行模塊內的語句) 如下:
#test.py
import spam #只在第一次導入時才執行spam.py內代碼,此處的顯示效果是只打印一次'from the spam.py',當然其他的頂級代碼也都被執行了,只不過沒有顯示效果.
import spam
import spam
import spam
'''
執行結果:
from the spam.py
'''
我們可以從sys.modules
中找到當前已經加載的模塊,sys.modules
的結果是一個字典,內部包含模塊名與模塊對象的映射,該字典決定了導入模塊時是否需要重新導入。
print(sys.modules)
# 輸出結果
{... ,'spam': <module 'spam' from 'E:\\python\\s17\\day05\\spam.py'>,...
首次import導入spam模塊干的事:
- 為源文件(spam模塊)創建新的命名空間,要spam中定義的函數和方法若是使用到了
global
時,訪問的就是這個名稱空間;- 在新創建的全稱空間中執行模塊中包含的代碼;
- 創建名字spam來引用該命名空間,這個名字和變量名沒什么區別,都是‘第一類的’,且使用spam.名字的方式可以訪問spam.py文件中定義的名字,spam.名字與test.py中的名字來自兩個完全不同的地方。
為模塊起別名
import spam as sm
為已經導入的模塊起別名的方式對編寫可擴展的代碼很有用,假設有兩個模塊xmlreader.py和csvreader.py,它們都定義了函數read_data(filename):用來從文件中讀取一些數據,但采用不同的輸入格式。可以編寫代碼來選擇性地挑選讀取模塊,例如:
if file_format == 'xml':
import xmlreader as reader
elif file_format == 'csv':
import csvreader as reader
data=reader.read_date(filename)
在一行導入多個模塊
import sys,os,re
3.2 from ... import ...
對比import spam,會將源文件的名稱空間'spam'帶到當前名稱空間中,使用時必須是spam.名字的方式;
而from 語句相當于import,也會創建新的名稱空間,但是將spam中的名字直接導入到當前的名稱空間中,在當前名稱空間中,直接使用名字就可以了。
from spam import read1,read2
這樣在當前位置直接使用read1和read2就好了,執行時,仍然以spam.py文件全局名稱空間
#測試一:導入的函數read1,執行時仍然回到spam.py中尋找全局變量money
#test.py
from spam import read1
money = 10
read1()
'''
執行結果:
from the spam.py
spam->read1->money 1000
'''
#測試二:導入的函數read2,執行時需要調用read1(),仍然回到spam.py中找read1()
#test.py
from spam import read2
def read1():
print('==========')
read2()
'''
執行結果:
from the spam.py
spam->read2 calling read
spam->read1->money 1000
'''
如果當前有重名的read1或read2,那么會覆蓋導入的read1和read2
也支持as起別名
from spam import read1 as read
也支持導入多行
from spam import (read1,
read2,
money)
*from spam import ***
from spam import * 把spam中所有的不是以下劃線(_)開頭的名字都導入到當前位置,大部分情況下我們的python程序不應該使用這種導入方式,因為你不知道你導入什么名字,很有可能會覆蓋掉你之前已經定義的名字。而且可讀性極其的差,在交互式環境中導入時沒有問題。
可以使用__all__
來控制 *
在spam.py中新增一行
__all__ = ['money','read1']
# 這樣在另外一個文件用from spam import * 就只能導入列表中存在的兩個名字
3.3 把模塊當做腳本執行
我們可以通過模塊的全局變量__name__
來查看模塊名:
當做腳本運行:
__name__ 等于 '__main__'
當做模塊導入:
__name__ = 文件名
作用:用來控制 .py文件在不同的應用場景下執行不同的邏輯
#fib.py
def fib(n): # write Fibonacci series up to n
a, b = 0, 1
while b < n:
print(b, end=' ')
a, b = b, a+b
print()
def fib2(n): # return Fibonacci series up to n
result = []
a, b = 0, 1
while b < n:
result.append(b)
a, b = b, a+b
return result
# 如果是當做腳本執行,則會執行下列代碼,當作模塊導入時,則不會執行下述代碼
if __name__ == "__main__":
import sys
fib(int(sys.argv[1]))
3.4 模塊搜索路徑
模塊的查找順序:
- 1.內存中已經加載的模塊
- 2.內置模塊
- 3.
sys.path
路徑中包含的模塊
需要特別注意的是:我們自定義的模塊名不應該與系統內置模塊重名。
在初始化后,python程序可以修改sys.path,路徑放到前面的優先于標準庫被加載。
import sys
sys.path.append('/a/b/c/d')
sys.path.insert(0,'/x/y/z') #排在前的目錄,優先被搜索
注意:搜索時按照sys.path中從左到右的順序查找,位于前的優先被查找,sys.path中還可能包含.zip歸檔文件和.egg文件,python會把.zip歸檔文件當成一個目錄去處理。
#windows下的路徑不加r開頭,會語法錯誤
sys.path.insert(0,r'C:\Users\Administrator\PycharmProjects\a')
至于.egg文件是由setuptools創建的包,這是按照第三方python庫和擴展時使用的一種常見格式,.egg文件實際上只是添加了額外元數據(如版本號,依賴項等)的.zip文件。
3.5 dir()函數
內建函數dir是用來查找模塊中定義的名字,返回一個有序字符串列表
import spam
dir(spam)
如果沒有參數,dir()列舉出當前定義的名字
dir()
不會列舉出內建函數或者變量的名字,它們都被定義到了標準模塊builtin中,可以列舉出它們,
import builtins
dir(builtins)
五、包
包是一種通過使用‘.模塊名’來組織python模塊名稱空間的方式。
無論是import形式還是from...import形式,凡是在導入語句中(而不是在使用時)遇到帶點的,都要第一時間提高警覺:這是關于包才有的導入語法;
包是目錄級的(文件夾級),文件夾是用來組成py文件(包的本質就是一個包含init.py文件的目錄)
import導入文件時,產生名稱空間中的名字來源于文件,import 包,產生的名稱空間的名字同樣來源于文件,即包下的init.py,導入包本質就是在導入該文件
注意事項:
1.關于包相關的導入語句也分為import和from ... import ...兩種,但是無論哪種,無論在什么位置,在導入時都必須遵循一個原則:凡是在導入時帶點的,點的左邊都必須是一個包,否則非法。可以帶有一連串的點,如item.subitem.subsubitem,但都必須遵循這個原則。
2.對于導入后,在使用時就沒有這種限制了,點的左邊可以是包,模塊,函數,類(它們都可以用點的方式調用自己的屬性)。
3.對比import item 和from item import name的應用場景:
如果我們想直接使用name那必須使用后者。
glance/ #Top-level package
├── __init__.py #Initialize the glance package
├── api #Subpackage for api
│ ├── __init__.py
│ ├── policy.py
│ └── versions.py
├── cmd #Subpackage for cmd
│ ├── __init__.py
│ └── manage.py
└── db #Subpackage for db
├── __init__.py
└── models.py
5.1 import
我們在與包glance同級別的文件中測試
import glance.db.models
glance.db.models.register_models('mysql')
5.2 from ... import ...
需要注意的是from后import導入的模塊,必須是明確的一個不能帶點,否則會有語法錯誤,如:from a import b.c是錯誤語法
我們在與包glance同級別的文件中測試
from glance.db import models
models.register_models('mysql')
from glance.db.models import register_models
register_models('mysql')
5.3 __init__.py
文件
不管是哪種方式,只要是第一次導入包或者是包的任何其他部分,都會依次執行包下的init.py文件(我們可以在每個包的文件內都打印一行內容來驗證一下),這個文件可以為空,但是也可以存放一些初始化包的代碼。
5.4 from glance.api import *
在講模塊時,我們已經討論過了從一個模塊內導入所有,此處我們研究從一個包導入所有。
此處是想從包api中導入所有,實際上該語句只會導入包api下init.py文件中定義的名字,我們可以在這個文件中定義all_:
#在__init__.py中定義
x=10
def func():
print('from api.__init.py')
__all__=['x','func','policy']
此時我們在于glance同級的文件中執行from glance.api import *就導入all中的內容(versions仍然不能導入)。
5.5 絕對導入和相對導入
我們的最頂級包glance是寫給別人用的,然后在glance包內部也會有彼此之間互相導入的需求,這時候就有絕對導入和相對導入兩種方式:
絕對導入:以glance作為起始
相對導入:用.或者..的方式最為起始(只能在一個包中使用,不能用于不同目錄內)
例如:我們在glance/api/version.py中想要導入glance/cmd/manage.py
在glance/api/version.py
#絕對導入
from glance.cmd import manage
manage.main()
#相對導入
from ..cmd import manage
manage.main()
測試結果:注意一定要在于glance同級的文件中測試
from glance.api import versions
注意:在使用pycharm時,有的情況會為你多做一些事情,這是軟件相關的東西,會影響你對模塊導入的理解,因而在測試時,一定要回到命令行去執行,模擬我們生產環境,你總不能拿著pycharm去上線代碼吧!!!
特別需要注意的是:可以用import導入內置或者第三方模塊(已經在sys.path中),但是要絕對避免使用import來導入自定義包的子模塊(沒有在sys.path中),應該使用from... import ...的絕對或者相對導入,且包的相對導入只能用from的形式。
5.6 單獨導入包
單獨導入包名稱時不會導入包中所有包含的所有子模塊,如:
#在與glance同級的test.py中
import glance
glance.cmd.manage.main()
'''
執行結果:
AttributeError: module 'glance' has no attribute 'cmd'
'''
解決方法一:
#在glance/__init__.py中寫:
import glance.cmd.manage
解決方法二:
1 #在glance/__init__.py中寫:
2 from . import cmd
3
4 #在glance/cmd/__init__.py中寫:
5 from . import manage
驗證:
1 #在于glance同級的test.py中執行
2 import glance
3 glance.cmd.manage.main()
六、re模塊
正則表達式常用匹配模式(元字符)
re模塊提供的方法
import re
#1
print(re.findall('e','alex make love')) #返回結果['e', 'e', 'e'],返回所有滿足匹配條件的結果,放在列表里
#2
print(re.search('e','alex make love').group()) #返回結果e,只到找到第一個匹配然后返回一個包含匹配信息的對象,該對象可以通過調用group()方法得到匹配的字符串,如果字符串沒有匹配,則返回None。
#3
print(re.match('e','alex make love')) #返回None,同search,不過在字符串開始處進行匹配,完全可以用search+^代替match
#4
print(re.split('[ab]','cadbcd')) #返回['c', 'd', 'cd'],先按'a'分割得到'c'和'dbcd',再對'c'和'dbcd'分別按'b'分割,得到['c', 'd', 'cd']
#5
print('===>',re.sub('a','A','alex make love')) #===> Alex mAke love,不指定n,默認替換所有
print('===>',re.sub('a','A','alex make love',1)) #===> Alex make love
print('===>',re.sub('a','A','alex make love',2)) #===> Alex mAke love #替換前兩個匹配
print('===>',re.sub('^(\w+)(.*?\s)(\w+)(.*?\s)(\w+)(.*?)$',r'\5\2\3\4\1','alex make love')) #===> love make alex #反向引用
#6
print('===>',re.subn('a','A','alex make love')) #===> ('Alex mAke love', 2),結果帶有總共替換的個數
#6
obj=re.compile('\d{2}')
print(obj.search('abc123eeee').group()) #返回12
print(obj.findall('abc123eeee')) #返回['12'],重用了obj
# 補充一
import re
print(re.findall("<(?P<tag_name>\w+)>\w+</(?P=tag_name)>","<h1>hello</h1>")) #['h1']
print(re.search("<(?P<tag_name>\w+)>\w+</(?P=tag_name)>","<h1>hello</h1>").group()) #<h1>hello</h1>
print(re.search("<(?P<tag_name>\w+)>\w+</(?P=tag_name)>","<h1>hello</h1>").groupdict()) #<h1>hello</h1>
print(re.search(r"<(\w+)>\w+</(\w+)>","<h1>hello</h1>").group())
print(re.search(r"<(\w+)>\w+</\1>","<h1>hello</h1>").group())
# 補充二
import re
print(re.findall(r'-?\d+\.?\d*',"1-12*(60+(-40.35/5)-(-4*3))")) #找出所有數字['1', '-12', '60', '-40.35', '5', '-4', '3']
#使用|,先匹配的先生效,|左邊是匹配小數,而findall最終結果是查看分組,所有即使匹配成功小數也不會存入結果
#而不是小數時,就去匹配(-?\d+),匹配到的自然就是,非小數的數,在此處即整數
print(re.findall(r"-?\d+\.\d*|(-?\d+)","1-2*(60+(-40.35/5)-(-4*3))")) #找出所有整數['1', '-2', '60', '', '5', '-4', '3']
#_*_coding:utf-8_*_
__author__ = 'Linhaifeng'
#在線調試工具:tool.oschina.net/regex/#
import re
s='''
http://www.baidu.com
egon@oldboyedu.com
你好
010-3141
'''
#最常規匹配
# content='Hello 123 456 World_This is a Regex Demo'
# res=re.match('Hello\s\d\d\d\s\d{3}\s\w{10}.*Demo',content)
# print(res)
# print(res.group())
# print(res.span())
#泛匹配
# content='Hello 123 456 World_This is a Regex Demo'
# res=re.match('^Hello.*Demo',content)
# print(res.group())
#匹配目標,獲得指定數據
# content='Hello 123 456 World_This is a Regex Demo'
# res=re.match('^Hello\s(\d+)\s(\d+)\s.*Demo',content)
# print(res.group()) #取所有匹配的內容
# print(res.group(1)) #取匹配的第一個括號內的內容
# print(res.group(2)) #去陪陪的第二個括號內的內容
#貪婪匹配:.*代表匹配盡可能多的字符
# import re
# content='Hello 123 456 World_This is a Regex Demo'
#
# res=re.match('^He.*(\d+).*Demo$',content)
# print(res.group(1)) #只打印6,因為.*會盡可能多的匹配,然后后面跟至少一個數字
#非貪婪匹配:?匹配盡可能少的字符
# import re
# content='Hello 123 456 World_This is a Regex Demo'
#
# res=re.match('^He.*?(\d+).*Demo$',content)
# print(res.group(1)) #只打印6,因為.*會盡可能多的匹配,然后后面跟至少一個數字
#匹配模式:.不能匹配換行符
content='''Hello 123456 World_This
is a Regex Demo
'''
# res=re.match('He.*?(\d+).*?Demo$',content)
# print(res) #輸出None
# res=re.match('He.*?(\d+).*?Demo$',content,re.S) #re.S讓.可以匹配換行符
# print(res)
# print(res.group(1))
#轉義:\
# content='price is $5.00'
# res=re.match('price is $5.00',content)
# print(res)
#
# res=re.match('price is \$5\.00',content)
# print(res)
#總結:盡量精簡,詳細的如下
# 盡量使用泛匹配模式.*
# 盡量使用非貪婪模式:.*?
# 使用括號得到匹配目標:用group(n)去取得結果
# 有換行符就用re.S:修改模式
#re.search:會掃描整個字符串,不會從頭開始,找到第一個匹配的結果就會返回
# import re
# content='Extra strings Hello 123 456 World_This is a Regex Demo Extra strings'
#
# res=re.match('Hello.*?(\d+).*?Demo',content)
# print(res) #輸出結果為None
#
# import re
# content='Extra strings Hello 123 456 World_This is a Regex Demo Extra strings'
#
# res=re.search('Hello.*?(\d+).*?Demo',content) #
# print(res.group(1)) #輸出結果為
#re.search:只要一個結果,匹配演練,
import re
content='''
<tbody>
<tr id="4766303201494371851675" class="even "><td><div class="hd"><span class="num">1</span><div class="rk "><span class="u-icn u-icn-75"></span></div></div></td><td class="rank"><div class="f-cb"><div class="tt"><a href="/song?id=476630320"></a><span data-res-id="476630320" "
# res=re.search('<a\shref=.*?<b\stitle="(.*?)".*?b>',content)
# print(res.group(1))
#re.findall:找到符合條件的所有結果
# res=re.findall('<a\shref=.*?<b\stitle="(.*?)".*?b>',content)
# for i in res:
# print(i)
#re.sub:字符串替換
import re
content='Extra strings Hello 123 456 World_This is a Regex Demo Extra strings'
# content=re.sub('\d+','',content)
# print(content)
#用\1取得第一個括號的內容
#用法:將123與456換位置
# import re
# content='Extra strings Hello 123 456 World_This is a Regex Demo Extra strings'
#
# # content=re.sub('(Extra.*?)(\d+)(\s)(\d+)(.*?strings)',r'\1\4\3\2\5',content)
# content=re.sub('(\d+)(\s)(\d+)',r'\3\2\1',content)
# print(content)
# import re
# content='Extra strings Hello 123 456 World_This is a Regex Demo Extra strings'
#
# res=re.search('Extra.*?(\d+).*strings',content)
# print(res.group(1))
# import requests,re
# respone=requests.get('https://book.douban.com/').text
# print(respone)
# print('======'*1000)
# print('======'*1000)
# print('======'*1000)
# print('======'*1000)
# res=re.findall('<li.*?cover.*?href="(.*?)".*?title="(.*?)">.*?more-meta.*?author">(.*?)</span.*?year">(.*?)</span.*?publisher">(.*?)</span.*?</li>',respone,re.S)
# # res=re.findall('<li.*?cover.*?href="(.*?)".*?more-meta.*?author">(.*?)</span.*?year">(.*?)</span.*?publisher">(.*?)</span>.*?</li>',respone,re.S)
#
#
# for i in res:
# print('%s %s %s %s' %(i[0].strip(),i[1].strip(),i[2].strip(),i[3].strip()))