第5天,協(xié)程函數(shù)、匿名函數(shù)、模塊與包、正則

目錄:

一、協(xié)程函數(shù)
    表達(dá)式形式的yield的用途
二、函數(shù)的遞歸調(diào)用
三、匿名函數(shù)lambda
    max()
    min()
    sorted()
    map()
    reduce()
    filter()
四、模塊
    1.什么是模塊
    2. 為何要使用模塊
    3. 導(dǎo)入模塊
        3.1 import
        3.2 from … import …
        3.3 把模塊當(dāng)做腳本執(zhí)行
        3.4 模塊搜索路徑
        3.5 dir()函數(shù)
五、包
    5.1 import
    5.2 from … import …
    5.3 __init__.py文件
    5.4 from glance.api import *
    5.5 絕對(duì)導(dǎo)入和相對(duì)導(dǎo)入
    5.6 單獨(dú)導(dǎo)入包
六、re模塊
    re模塊提供的方法

上一節(jié)課生成器還有一些知識(shí)點(diǎn)沒講到,接下來補(bǔ)充;

一、協(xié)程函數(shù)

生成器:yield關(guān)鍵字的另外一種用法
yield的語句形式有兩種:

  • yield n //n為yield的返回值
  • x = yield n //yield的表達(dá)式形式,n為yield返回值

現(xiàn)在模擬去餐廳吃飯,服務(wù)員上一盤菜,你就吃一盤,然后等著服務(wù)員再繼續(xù)上菜

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是一個(gè)生成器對(duì)象
next(g)            # 第一次必須給yield傳一個(gè)空值
res = g.send('餃子')      #g.send('餃子')就相當(dāng)于服務(wù)員上菜
print(res)
g.send('包子')
res1 = g.send('餅子')
print(res1)

輸出結(jié)果:

alex ready eat something ...
alex eat a 餃子
['餃子']
alex eat a 包子
alex eat a 餅子
['餃子', '包子', '餅子']

執(zhí)行流程解析:

  1. g=eater('alex') ,g是一個(gè)生成器對(duì)象,這一步并不會(huì)使eater()函數(shù)執(zhí)行;
  2. 遇到next(g),會(huì)觸發(fā)eater('alex')開始執(zhí)行;
  3. eater('alex')執(zhí)行中遇到y(tǒng)ield關(guān)鍵字時(shí),函數(shù)執(zhí)行暫停在此處,并返回yield后面的值food_list(此時(shí)food_list是一個(gè)空列表),跳出到函數(shù)外面觸發(fā)eater('alex')執(zhí)行的一行(next(g)所在的那一行);
  4. next(g)接收yield返回的food_list(此時(shí)是空列表),并繼續(xù)往下執(zhí)行;
  5. 當(dāng)遇到g.send('餃子')時(shí),會(huì)將'餃子’傳給yield,并觸發(fā)eater('alex')從上次暫停的位置(yield處)開始執(zhí)行,yield接收g.send('餃子')傳進(jìn)來的值,并賦值給food (此時(shí)food = '餃子'),然后eater('alex')函數(shù)繼續(xù)往下執(zhí)行;
  6. while True死循環(huán),循環(huán)到下一次yield時(shí),暫停執(zhí)行,將food_list返回給g.send('餃子'),此時(shí)的food_list加入了food(food='餃子'),并將food_list賦值給res;
  7. 繼續(xù)往下執(zhí)行,每當(dāng)遇到g.send()就會(huì)重復(fù)第5步的流程,遇到y(tǒng)ield就會(huì)重復(fù)第6步的流程;

總結(jié):

  • next(g)g.send('x')都能觸發(fā)生成器函數(shù)的執(zhí)行,不同的是send可以給yiled傳值;
  • 第一次給yield傳值,必須傳空值,next(g)也相當(dāng)于給yield傳了一個(gè)空值,所以next(g)也可用g.send(None)替換;
  • 由于給每個(gè)生成器函數(shù)傳值之前必須先傳一個(gè)空值,這樣很容易讓人忘掉此步驟,基于此,可以將此步驟寫成一個(gè)裝飾器去實(shí)現(xiàn);這樣就可以在多處使用了;請(qǐng)看下面的實(shí)現(xiàn)代碼 。

應(yīng)用如下:

def init(func):
    '''這個(gè)裝飾器的作用是初始化其他生成器函數(shù)''' 
    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('牛奶'))

輸出結(jié)果:

alex ready eat something ...
alex eat a 餃子
alex eat a 包子
alex eat a 餅子
alex eat a 牛奶
['餃子', '包子', '餅子', '牛奶']

可以看出,注釋掉res = next(g)后,執(zhí)行結(jié)果與之前無異。

表達(dá)式形式的yield的用途

# 實(shí)現(xiàn)類似grep -rl ‘xxx’ /root 的功能;
# 實(shí)現(xiàn)的功能是查找出root目錄下所有包含xxx的文件,并將文件名返回;

import os

def init(func):
    '''這個(gè)裝飾器的作用:初始化一個(gè)生成器'''
    def wrapper(*args,**kwargs):
        res = func(*args,**kwargs)
        next(res)
        return res
    return wrapper

init
def ergodic(target):
    '''提取傳入的目錄下所有文件的絕對(duì)路徑,發(fā)送給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()傳遞的文件絕對(duì)路徑,打開,并讀取每行一內(nèi)容,傳遞給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(需要搜索的關(guān)鍵字)是否在行中,\
    然后將文件絕對(duì)路徑傳給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():
    '''打印文件絕對(duì)路徑'''
    while True:
        file_abs_path = yield
        print(file_abs_path)

gen = ergodic(opener(grep(printer(),'python')))
path = r'E:\python\s17\day05\練習(xí)\a'
gen.send(path)

執(zhí)行結(jié)果:

E:\python\s17\day05\練習(xí)\a\a.txt
E:\python\s17\day05\練習(xí)\a\b\b.txt
E:\python\s17\day05\練習(xí)\a\b\c\d\d.txt
E:\python\s17\day05\練習(xí)\a\b1\b.txt

二、函數(shù)的遞歸調(diào)用

在函數(shù)調(diào)用過程中,直接或間接地調(diào)用了函數(shù)本身, 這就是函數(shù)的遞歸調(diào)用,如下示例:

def f1():
    print('from f1')
    f1()

f1()

注:上述代碼,f1()的函數(shù)體內(nèi)又調(diào)用了它本身,當(dāng)在外部調(diào)用f1()時(shí),會(huì)產(chǎn)生一個(gè)死循環(huán)(python默認(rèn)遞歸調(diào)用設(shè)定為1000次,可以通過下述方法獲取);

import sys
print(sys.getrecursionlimit())   #獲取python遞歸調(diào)用限制次數(shù)
sys.setrecursionlimit(100000)    #設(shè)定遞歸調(diào)用限制次數(shù)
print(sys.getrecursionlimit())

# 輸出結(jié)果:
1000
100000

遞歸的特性:

  1. 必須有一個(gè)明確的結(jié)束條件;
  2. 每次進(jìn)入更深一層遞歸時(shí),問題規(guī)模相比上次遞歸都應(yīng)該有所減少;
  3. 遞歸效率不高,遞歸層次過多會(huì)導(dǎo)致棧溢出(在計(jì)算機(jī)中,函數(shù)調(diào)用是通過棧(stack)這種數(shù)據(jù)結(jié)構(gòu)實(shí)現(xiàn)的,每當(dāng)進(jìn)入一函數(shù)調(diào)用,棧就會(huì)加一層棧幀,每當(dāng)函數(shù)返回,棧就會(huì)減一層幀。由于棧的大小不是無限的,所以遞歸調(diào)用的次數(shù)過多,會(huì)導(dǎo)致棧溢出)
    堆棧掃盲,請(qǐng)點(diǎn)擊查看

遞歸函數(shù)實(shí)際應(yīng)用,二分查找:

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數(shù)字列表中

輸出結(jié)果:

[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
您要查找的數(shù)字: 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

三、匿名函數(shù)lambda

匿名函數(shù)就是不需要顯式的指定函數(shù)

python 使用 lambda 來創(chuàng)建匿名函數(shù)。

  • lambda只是一個(gè)表達(dá)式,函數(shù)體比def簡(jiǎn)單很多。
  • lambda的主體是一個(gè)表達(dá)式,而不是一個(gè)代碼塊。僅僅能lambda表達(dá)式中封裝有限的邏輯進(jìn)去。
  • lambda函數(shù)擁有自己的命名空間,且不能訪問自有參數(shù)列表之外或全局命名空間里的參數(shù)。
  • 雖然lambda函數(shù)看起來只能寫一行,卻不等同于C或C++的內(nèi)聯(lián)函數(shù),后者的目的是調(diào)用小函數(shù)時(shí)不占用棧內(nèi)存從而增加運(yùn)行效率。

語法
lambda函數(shù)的語法只包含一個(gè)語句,如下:
lambda [arg1 [,arg2,.....argn]]:expression
如下實(shí)例:

#!/usr/bin/python
# -*- coding: UTF-8 -*-
 
# 可寫函數(shù)說明
sum = lambda arg1, arg2: arg1 + arg2;
 
# 調(diào)用sum函數(shù)
print("相加后的值為 : ", sum( 10, 20 ))
print("相加后的值為 : ", sum( 20, 20 ))

# 以上實(shí)例輸出結(jié)果:
相加后的值為 :  30
相加后的值為 :  40
def calc(n):
    return n**n
print(calc(3))    # 結(jié)果27

# 換成匿名函數(shù)
calc1=lambda x:x**x
print(cal1c(3))   # 結(jié)果27

lambda 會(huì)將:冒號(hào)后面的表達(dá)式結(jié)果返回

這么用,感覺好像匿名函數(shù)也沒有什么用。
不過匿名函數(shù)主要是和其它函數(shù)搭配使用的。
如下,將匿名函數(shù)和內(nèi)置函數(shù)max()、min()、sorted()、zip()。。。結(jié)合起來使用

max()

max()函數(shù)的功能是獲取序列最大值
以下例子用max()函數(shù)獲取工資最高的人名

salaries={
    'egon':3000,
    'alex':100000000,
    'wupeiqi':10000,
    'yuanhao':2000
}

def func(k):
    return salaries[k]

res = max(salaries,key=func)     #1
print(res)

# 輸出結(jié)果:
alex

#1 max()函數(shù)會(huì)遍歷字典salaries中的key,將key當(dāng)作參數(shù)傳給函數(shù)func() ,然后函數(shù)func()返回key對(duì)應(yīng)的value然后賦值給max()括號(hào)中的key,并根據(jù)這個(gè)key進(jìn)行比較,然后返回工資最高的人名

將以上代碼通過匿名函數(shù)簡(jiǎn)化:

salaries={
    'egon':3000,
    'alex':100000000,
    'wupeiqi':10000,
    'yuanhao':2000
}

res = max(salaries,key=lambda k:salaries[k])
print(res)

# 輸出結(jié)果:
alex

min()

獲取序列最小值,用法和max()函數(shù)相同

salaries={
    'egon':3000,
    'alex':100000000,
    'wupeiqi':10000,
    'yuanhao':2000
}

res = min(salaries,key=lambda x:salaries[x])
print(res)

# 輸出結(jié)果:
yuanhao

sorted()

序列化數(shù)據(jù)排序,默認(rèn)是從小到大排序,加reverse=True關(guān)鍵字可以從大到小排序,用法也和max()類似

以下示例,輸出工資從低到高的名字

salaries={
    'egon':3000,
    'alex':100000000,
    'wupeiqi':10000,
    'yuanhao':2000
}

print(sorted(salaries,key=lambda x:salaries[x]))

# 輸出結(jié)果:
['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))

# 輸出結(jié)果:
['alex', 'wupeiqi', 'egon', 'yuanhao']

map()

map()函數(shù)接收兩個(gè)參數(shù),一個(gè)是函數(shù),一個(gè)是iterable(可迭代對(duì)象),map將傳入的函數(shù)依次作用到序列的每個(gè)元素,并把結(jié)果作為新的iterable返回。

以下示例 ,將不符合規(guī)范的名字變成符合規(guī)范的首字母大寫的名字

name_li = ['adam','LISA','barI']
print(list(map(lambda x:x.capitalize(),name_li)))

# 輸出結(jié)果:
['Adam', 'Lisa', 'Bari']

將每個(gè)名字末尾加上指定字符

name_list = ['alex','wupeiqi','yuanhao']
print(list(map(lambda x:x+'_god',name_list)))

# 輸出結(jié)果:
['alex_god', 'wupeiqi_god', 'yuanhao_god']

reduce()

python2中reduce()還是一個(gè)單獨(dú)的內(nèi)置函數(shù);python3中則將reduce()集成到functools包中了,使用之前需要先導(dǎo)入from functools import reduce

reduce把一個(gè)函數(shù)作用在一個(gè)序列[x1, x2, x3, ...]上,這個(gè)函數(shù)必須接收兩個(gè)參數(shù),reduce把結(jié)果繼續(xù)和序列的下一個(gè)元素做累積計(jì)算,其效果就是:

reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)

# f假設(shè)是一個(gè)函數(shù)

如下示例,對(duì)一個(gè)序列求和

from functools import reduce
num_li = [1,2,3,4,5]
res = reduce(lambda x,y:x+y,num_li)
print(res)

# 輸出結(jié)果:
15

reduce一個(gè)初始值:

from functools import reduce
num_li = [1,2,3,4,5]
res = reduce(lambda x,y:x+y,num_li,10)
print(res)

# 輸出結(jié)果:
25

map()reduce()結(jié)合使用
將字典中的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')))

# 輸出結(jié)果:
13579

filter()

filter函數(shù)用于過濾序列
map()類似,filter()也接收一個(gè)函數(shù)和一個(gè)序列。和map()不同的是,filter()把傳入的函數(shù)依次作用于每個(gè)元素,然后根據(jù)返回值是True還是False決定保留還是丟棄該元素。

過慮以‘god’結(jié)尾的名字

name_li = ['alex_god', 'wupeiqi_god', 'yuanhao_god','egon']

res = filter(lambda x:x.endswith('god'),name_li)
    # res 得到的是一個(gè)filter對(duì)象
    # <filter object at 0x000...748>
print(list(res))

# 輸出結(jié)果:
['alex_god', 'wupeiqi_god', 'yuanhao_god']

注意:
map()filter()函數(shù)返回的是一個(gè)Iterator,也就是一個(gè)惰性序列,所以要強(qiáng)迫map()filter()完成計(jì)算結(jié)果,需要用list()函數(shù)獲得所有結(jié)果并返回list。

四、模塊

1.什么是模塊

一個(gè)模塊就是一個(gè)包含了python定義和聲明的文件,文件名就是模塊名字加上.py的后綴

2. 為何要使用模塊

  • 如果你退出python解釋器然后重新進(jìn)入,那么你之前定義的函數(shù)或者變量都將丟失,因此我們通常將程序?qū)懙轿募幸员阌谰帽4嫦聛恚枰獣r(shí)就通過python test.py方式去執(zhí)行,此時(shí)test.py被稱為腳本script。

  • 隨著程序的發(fā)展,功能越來越多,為了方便管理,我們通常將程序分成一個(gè)個(gè)的文件,這樣做程序的結(jié)構(gòu)更清晰,方便管理。這時(shí)我們不僅僅可以把這些文件當(dāng)做腳本去執(zhí)行,還可以把他們當(dāng)做模塊來導(dǎo)入到其他的模塊中,實(shí)現(xiàn)了功能的重復(fù)利用。

3. 導(dǎo)入模塊

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

模塊可以包含可執(zhí)行的語句和函數(shù)的定義,這些語句的目的是初始化模塊,它們只在模塊名第一次遇到導(dǎo)入import語句時(shí)才執(zhí)行,(import語句可以在程序中的任意位置使用,且針對(duì)同一個(gè)模塊可以import多次,為了防止重復(fù)導(dǎo)入,python的優(yōu)化手段是:第一次導(dǎo)入后就將模塊名加載到內(nèi)存中,后續(xù)的import語句僅是對(duì)已經(jīng)加載到內(nèi)存中的模塊對(duì)象增加了一次引用,不會(huì)重新執(zhí)行模塊內(nèi)的語句) 如下:

#test.py
import spam #只在第一次導(dǎo)入時(shí)才執(zhí)行spam.py內(nèi)代碼,此處的顯示效果是只打印一次'from the spam.py',當(dāng)然其他的頂級(jí)代碼也都被執(zhí)行了,只不過沒有顯示效果.
import spam
import spam
import spam

'''
執(zhí)行結(jié)果:
from the spam.py
'''

我們可以從sys.modules中找到當(dāng)前已經(jīng)加載的模塊,sys.modules的結(jié)果是一個(gè)字典,內(nèi)部包含模塊名與模塊對(duì)象的映射,該字典決定了導(dǎo)入模塊時(shí)是否需要重新導(dǎo)入。

print(sys.modules)

# 輸出結(jié)果
{... ,'spam': <module 'spam' from 'E:\\python\\s17\\day05\\spam.py'>,...

首次import導(dǎo)入spam模塊干的事:

  1. 為源文件(spam模塊)創(chuàng)建新的命名空間,要spam中定義的函數(shù)和方法若是使用到了global時(shí),訪問的就是這個(gè)名稱空間;
  2. 在新創(chuàng)建的全稱空間中執(zhí)行模塊中包含的代碼;
  3. 創(chuàng)建名字spam來引用該命名空間,這個(gè)名字和變量名沒什么區(qū)別,都是‘第一類的’,且使用spam.名字的方式可以訪問spam.py文件中定義的名字,spam.名字與test.py中的名字來自兩個(gè)完全不同的地方。

為模塊起別名

import spam as sm

為已經(jīng)導(dǎo)入的模塊起別名的方式對(duì)編寫可擴(kuò)展的代碼很有用,假設(shè)有兩個(gè)模塊xmlreader.py和csvreader.py,它們都定義了函數(shù)read_data(filename):用來從文件中讀取一些數(shù)據(jù),但采用不同的輸入格式。可以編寫代碼來選擇性地挑選讀取模塊,例如:

if file_format == 'xml':
    import xmlreader as reader
elif file_format == 'csv':
    import csvreader as reader
data=reader.read_date(filename)

在一行導(dǎo)入多個(gè)模塊

import sys,os,re

3.2 from ... import ...

對(duì)比import spam,會(huì)將源文件的名稱空間'spam'帶到當(dāng)前名稱空間中,使用時(shí)必須是spam.名字的方式;

而from 語句相當(dāng)于import,也會(huì)創(chuàng)建新的名稱空間,但是將spam中的名字直接導(dǎo)入到當(dāng)前的名稱空間中,在當(dāng)前名稱空間中,直接使用名字就可以了。

from spam import read1,read2

這樣在當(dāng)前位置直接使用read1和read2就好了,執(zhí)行時(shí),仍然以spam.py文件全局名稱空間


#測(cè)試一:導(dǎo)入的函數(shù)read1,執(zhí)行時(shí)仍然回到spam.py中尋找全局變量money
#test.py
from spam import read1
money = 10
read1()

'''
執(zhí)行結(jié)果:
from the spam.py
spam->read1->money 1000
'''
#測(cè)試二:導(dǎo)入的函數(shù)read2,執(zhí)行時(shí)需要調(diào)用read1(),仍然回到spam.py中找read1()
#test.py
from spam import read2
def read1():
    print('==========')
read2()

'''
執(zhí)行結(jié)果:
from the spam.py
spam->read2 calling read
spam->read1->money 1000
'''

如果當(dāng)前有重名的read1或read2,那么會(huì)覆蓋導(dǎo)入的read1和read2

也支持as起別名

from spam import read1 as read

也支持導(dǎo)入多行

from spam import (read1,
                  read2,
                  money)

*from spam import ***
from spam import * 把spam中所有的不是以下劃線(_)開頭的名字都導(dǎo)入到當(dāng)前位置,大部分情況下我們的python程序不應(yīng)該使用這種導(dǎo)入方式,因?yàn)?/em>你不知道你導(dǎo)入什么名字,很有可能會(huì)覆蓋掉你之前已經(jīng)定義的名字。而且可讀性極其的差,在交互式環(huán)境中導(dǎo)入時(shí)沒有問題。

可以使用__all__來控制 *
在spam.py中新增一行

__all__ = ['money','read1']
# 這樣在另外一個(gè)文件用from spam import * 就只能導(dǎo)入列表中存在的兩個(gè)名字

3.3 把模塊當(dāng)做腳本執(zhí)行

我們可以通過模塊的全局變量__name__來查看模塊名:
當(dāng)做腳本運(yùn)行:

__name__ 等于 '__main__'

當(dāng)做模塊導(dǎo)入:

__name__ = 文件名

作用:用來控制 .py文件在不同的應(yīng)用場(chǎng)景下執(zhí)行不同的邏輯

#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

# 如果是當(dāng)做腳本執(zhí)行,則會(huì)執(zhí)行下列代碼,當(dāng)作模塊導(dǎo)入時(shí),則不會(huì)執(zhí)行下述代碼
if __name__ == "__main__":
    import sys
    fib(int(sys.argv[1]))

3.4 模塊搜索路徑

模塊的查找順序:

  • 1.內(nèi)存中已經(jīng)加載的模塊
  • 2.內(nèi)置模塊
  • 3.sys.path路徑中包含的模塊

需要特別注意的是:我們自定義的模塊名不應(yīng)該與系統(tǒng)內(nèi)置模塊重名。

在初始化后,python程序可以修改sys.path,路徑放到前面的優(yōu)先于標(biāo)準(zhǔn)庫被加載。

import sys
sys.path.append('/a/b/c/d')
sys.path.insert(0,'/x/y/z') #排在前的目錄,優(yōu)先被搜索

注意:搜索時(shí)按照sys.path中從左到右的順序查找,位于前的優(yōu)先被查找,sys.path中還可能包含.zip歸檔文件和.egg文件,python會(huì)把.zip歸檔文件當(dāng)成一個(gè)目錄去處理。

#windows下的路徑不加r開頭,會(huì)語法錯(cuò)誤
sys.path.insert(0,r'C:\Users\Administrator\PycharmProjects\a')

至于.egg文件是由setuptools創(chuàng)建的包,這是按照第三方python庫和擴(kuò)展時(shí)使用的一種常見格式,.egg文件實(shí)際上只是添加了額外元數(shù)據(jù)(如版本號(hào),依賴項(xiàng)等)的.zip文件。

3.5 dir()函數(shù)

內(nèi)建函數(shù)dir是用來查找模塊中定義的名字,返回一個(gè)有序字符串列表

import spam
dir(spam)

如果沒有參數(shù),dir()列舉出當(dāng)前定義的名字

dir()不會(huì)列舉出內(nèi)建函數(shù)或者變量的名字,它們都被定義到了標(biāo)準(zhǔn)模塊builtin中,可以列舉出它們,

import builtins
dir(builtins)

五、包

包是一種通過使用‘.模塊名’來組織python模塊名稱空間的方式。

  1. 無論是import形式還是from...import形式,凡是在導(dǎo)入語句中(而不是在使用時(shí))遇到帶點(diǎn)的,都要第一時(shí)間提高警覺:這是關(guān)于包才有的導(dǎo)入語法;

  2. 包是目錄級(jí)的(文件夾級(jí)),文件夾是用來組成py文件(包的本質(zhì)就是一個(gè)包含init.py文件的目錄)

  3. import導(dǎo)入文件時(shí),產(chǎn)生名稱空間中的名字來源于文件,import 包,產(chǎn)生的名稱空間的名字同樣來源于文件,即包下的init.py,導(dǎo)入包本質(zhì)就是在導(dǎo)入該文件

注意事項(xiàng):
1.關(guān)于包相關(guān)的導(dǎo)入語句也分為import和from ... import ...兩種,但是無論哪種,無論在什么位置,在導(dǎo)入時(shí)都必須遵循一個(gè)原則:凡是在導(dǎo)入時(shí)帶點(diǎn)的,點(diǎn)的左邊都必須是一個(gè)包,否則非法。可以帶有一連串的點(diǎn),如item.subitem.subsubitem,但都必須遵循這個(gè)原則。
2.對(duì)于導(dǎo)入后,在使用時(shí)就沒有這種限制了,點(diǎn)的左邊可以是包,模塊,函數(shù),類(它們都可以用點(diǎn)的方式調(diào)用自己的屬性)。
3.對(duì)比import item 和from item import name的應(yīng)用場(chǎng)景:
如果我們想直接使用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

我們?cè)谂c包glance同級(jí)別的文件中測(cè)試

import glance.db.models
glance.db.models.register_models('mysql') 

5.2 from ... import ...

需要注意的是from后import導(dǎo)入的模塊,必須是明確的一個(gè)不能帶點(diǎn),否則會(huì)有語法錯(cuò)誤,如:from a import b.c是錯(cuò)誤語法

我們?cè)谂c包glance同級(jí)別的文件中測(cè)試

from glance.db import models
models.register_models('mysql')
 
from glance.db.models import register_models
register_models('mysql')

5.3 __init__.py文件

不管是哪種方式,只要是第一次導(dǎo)入包或者是包的任何其他部分,都會(huì)依次執(zhí)行包下的init.py文件(我們可以在每個(gè)包的文件內(nèi)都打印一行內(nèi)容來驗(yàn)證一下),這個(gè)文件可以為空,但是也可以存放一些初始化包的代碼。

5.4 from glance.api import *

在講模塊時(shí),我們已經(jīng)討論過了從一個(gè)模塊內(nèi)導(dǎo)入所有,此處我們研究從一個(gè)包導(dǎo)入所有

此處是想從包api中導(dǎo)入所有,實(shí)際上該語句只會(huì)導(dǎo)入包api下init.py文件中定義的名字,我們可以在這個(gè)文件中定義all_:

#在__init__.py中定義
x=10

def func():
    print('from api.__init.py')

__all__=['x','func','policy']

此時(shí)我們?cè)谟趃lance同級(jí)的文件中執(zhí)行from glance.api import *就導(dǎo)入all中的內(nèi)容(versions仍然不能導(dǎo)入)。

5.5 絕對(duì)導(dǎo)入和相對(duì)導(dǎo)入

我們的最頂級(jí)包glance是寫給別人用的,然后在glance包內(nèi)部也會(huì)有彼此之間互相導(dǎo)入的需求,這時(shí)候就有絕對(duì)導(dǎo)入和相對(duì)導(dǎo)入兩種方式:

絕對(duì)導(dǎo)入:以glance作為起始

相對(duì)導(dǎo)入:用.或者..的方式最為起始(只能在一個(gè)包中使用,不能用于不同目錄內(nèi))

例如:我們?cè)趃lance/api/version.py中想要導(dǎo)入glance/cmd/manage.py

在glance/api/version.py

#絕對(duì)導(dǎo)入
from glance.cmd import manage
manage.main()

#相對(duì)導(dǎo)入
from ..cmd import manage
manage.main()

測(cè)試結(jié)果:注意一定要在于glance同級(jí)的文件中測(cè)試

from glance.api import versions

注意:在使用pycharm時(shí),有的情況會(huì)為你多做一些事情,這是軟件相關(guān)的東西,會(huì)影響你對(duì)模塊導(dǎo)入的理解,因而在測(cè)試時(shí),一定要回到命令行去執(zhí)行,模擬我們生產(chǎn)環(huán)境,你總不能拿著pycharm去上線代碼吧!!!

特別需要注意的是:可以用import導(dǎo)入內(nèi)置或者第三方模塊(已經(jīng)在sys.path中),但是要絕對(duì)避免使用import來導(dǎo)入自定義包的子模塊(沒有在sys.path中),應(yīng)該使用from... import ...的絕對(duì)或者相對(duì)導(dǎo)入,且包的相對(duì)導(dǎo)入只能用from的形式。

5.6 單獨(dú)導(dǎo)入包

單獨(dú)導(dǎo)入包名稱時(shí)不會(huì)導(dǎo)入包中所有包含的所有子模塊,如:

#在與glance同級(jí)的test.py中
import glance
glance.cmd.manage.main()

'''
執(zhí)行結(jié)果:
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

驗(yàn)證:

1 #在于glance同級(jí)的test.py中執(zhí)行
2 import glance
3 glance.cmd.manage.main()

六、re模塊

正則表達(dá)式常用匹配模式(元字符)

匹配模式

re模塊提供的方法

import re
#1
print(re.findall('e','alex make love'))   #返回結(jié)果['e', 'e', 'e'],返回所有滿足匹配條件的結(jié)果,放在列表里

#2
print(re.search('e','alex make love').group()) #返回結(jié)果e,只到找到第一個(gè)匹配然后返回一個(gè)包含匹配信息的對(duì)象,該對(duì)象可以通過調(diào)用group()方法得到匹配的字符串,如果字符串沒有匹配,則返回None。

#3
print(re.match('e','alex make love'))    #返回None,同search,不過在字符串開始處進(jìn)行匹配,完全可以用search+^代替match

#4
print(re.split('[ab]','cadbcd')) #返回['c', 'd', 'cd'],先按'a'分割得到'c'和'dbcd',再對(duì)'c'和'dbcd'分別按'b'分割,得到['c', 'd', 'cd']

#5
print('===>',re.sub('a','A','alex make love')) #===> Alex mAke love,不指定n,默認(rè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 #替換前兩個(gè)匹配
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),結(jié)果帶有總共替換的個(gè)數(shù)

#6
obj=re.compile('\d{2}')

print(obj.search('abc123eeee').group()) #返回12
print(obj.findall('abc123eeee')) #返回['12'],重用了obj
# 補(bǔ)充一
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())
# 補(bǔ)充二
import re

print(re.findall(r'-?\d+\.?\d*',"1-12*(60+(-40.35/5)-(-4*3))")) #找出所有數(shù)字['1', '-12', '60', '-40.35', '5', '-4', '3']

#使用|,先匹配的先生效,|左邊是匹配小數(shù),而findall最終結(jié)果是查看分組,所有即使匹配成功小數(shù)也不會(huì)存入結(jié)果
#而不是小數(shù)時(shí),就去匹配(-?\d+),匹配到的自然就是,非小數(shù)的數(shù),在此處即整數(shù)

print(re.findall(r"-?\d+\.\d*|(-?\d+)","1-2*(60+(-40.35/5)-(-4*3))")) #找出所有整數(shù)['1', '-2', '60', '', '5', '-4', '3']
#_*_coding:utf-8_*_
__author__ = 'Linhaifeng'
#在線調(diào)試工具:tool.oschina.net/regex/#
import re

s='''
http://www.baidu.com
egon@oldboyedu.com
你好
010-3141
'''

#最常規(guī)匹配
# 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())


#匹配目標(biāo),獲得指定數(shù)據(jù)

# content='Hello 123 456 World_This is a Regex Demo'
# res=re.match('^Hello\s(\d+)\s(\d+)\s.*Demo',content)
# print(res.group()) #取所有匹配的內(nèi)容
# print(res.group(1)) #取匹配的第一個(gè)括號(hào)內(nèi)的內(nèi)容
# print(res.group(2)) #去陪陪的第二個(gè)括號(hào)內(nèi)的內(nèi)容



#貪婪匹配:.*代表匹配盡可能多的字符
# import re
# content='Hello 123 456 World_This is a Regex Demo'
#
# res=re.match('^He.*(\d+).*Demo$',content)
# print(res.group(1)) #只打印6,因?yàn)?*會(huì)盡可能多的匹配,然后后面跟至少一個(gè)數(shù)字


#非貪婪匹配:?匹配盡可能少的字符
# import re
# content='Hello 123 456 World_This is a Regex Demo'
#
# res=re.match('^He.*?(\d+).*Demo$',content)
# print(res.group(1)) #只打印6,因?yàn)?*會(huì)盡可能多的匹配,然后后面跟至少一個(gè)數(shù)字


#匹配模式:.不能匹配換行符
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))


#轉(zhuǎn)義:\

# 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)


#總結(jié):盡量精簡(jiǎn),詳細(xì)的如下
    # 盡量使用泛匹配模式.*
    # 盡量使用非貪婪模式:.*?
    # 使用括號(hào)得到匹配目標(biāo):用group(n)去取得結(jié)果
    # 有換行符就用re.S:修改模式



#re.search:會(huì)掃描整個(gè)字符串,不會(huì)從頭開始,找到第一個(gè)匹配的結(jié)果就會(huì)返回

# 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) #輸出結(jié)果為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)) #輸出結(jié)果為



#re.search:只要一個(gè)結(jié)果,匹配演練,
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">![](http://p1.music.126.net/Wl7T1LBRhZFg0O26nnR2iQ==/19217264230385030.jpg?param=50y50&quality=100)</a><span data-res-id="476630320" "
# res=re.search('<a\shref=.*?<b\stitle="(.*?)".*?b>',content)
# print(res.group(1))


#re.findall:找到符合條件的所有結(jié)果
# 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取得第一個(gè)括號(hào)的內(nèi)容
#用法:將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()))
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,837評(píng)論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,196評(píng)論 3 414
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,688評(píng)論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,654評(píng)論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,456評(píng)論 6 406
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 54,955評(píng)論 1 321
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,044評(píng)論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,195評(píng)論 0 287
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,725評(píng)論 1 333
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,608評(píng)論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,802評(píng)論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,318評(píng)論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,048評(píng)論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,422評(píng)論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,673評(píng)論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,424評(píng)論 3 390
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,762評(píng)論 2 372

推薦閱讀更多精彩內(nèi)容