協程函數、面向過程程式設計、子產品與包
一、協程函數
1 協程函數的定義
協程函數就是使用了yield表達式形成的生成器
執行個體:
def eater(name):
print('%s eat food' %name)
while True:
food = yield
print('done')
g = eater('Yim')
print(g)
#執行結果:
<generator object eater at 0x000000000227FEB8> #證明g現在是一個生成器函數
2 協程函數的指派
第一階段:next()初始化函數,讓函數暫停在yield位置
第二階段:send()給yield傳值
next()和send()都是讓函數在上次暫停的位置繼續執行。next是讓函數初始化,send會給yield指派并觸發下一次代碼執行
執行個體:
def eater(name):
print('%s start to eat food' %name)
while True:
food = yield
print('%s eat %s' %(name,food))
e = eater('Yim')
next(e) #初始化,等同于e.send(None)
e.send('米飯') #給yield傳值
e.send('大餅')
#執行結果
Yim start to eat food
Yim eat 米飯
Yim eat 大餅
def eater(name):
print('%s start to eat food' %name)
food_list = []
while True:
food = yield food_list
food_list.append(food)
print('%s eat %s' %(name,food))
e = eater('Yim')
next(e) #初始化
print(e.send('米飯')) #1、給yield傳值 2、繼續往下執行,直到再次碰到yield,然後暫停并且把yield後的傳回值當做本次調用的傳回值
print(e.send('大餅'))
#執行結果
Yim start to eat food
Yim eat 米飯
['米飯']
Yim eat 大餅
['米飯', '大餅']
3 用裝飾器初始化協程函數
def init(func):
def wrapper(*args,**kwargs):
res = func(*args,**kwargs)
next(res)
return res
return wrapper
@init
def eater(name):
print('%s start to eat food' %name)
food_list = []
while True:
food = yield food_list
food_list.append(food)
print('%s eat %s' %(name,food))
e = eater('Yim')
print(e.send('米飯'))
print(e.send('大餅'))
二、 面向過程程式設計
面向過程的程式設計思想:流水線式的程式設計思想,在設計程式時,需要把整個流程設計出來
優點:
體系結構更清晰
簡化程式的複雜度
缺點:
可擴充性差
應用場景:
Linux核心、git、httpd、shell腳本
執行個體:
# grep -rl 'error' /dir
import os
#初始化裝飾器
def init(func):
def wrapper(*args,**kwargs):
g=func(*args,**kwargs)
next(g)
return g
return wrapper
#1、找到所有檔案的絕對路徑 --os.walk
@init
def search(target):
while True:
filepath=yield
g=os.walk(filepath)
for pardir,_,files in g:
for file in files: #對最後一個元素進行周遊,這些都是檔案
abspath=r'%s\%s' %(pardir,file)
target.send(abspath)
#2、打開檔案 --open
@init
def opener(target):
while True:
abspath=yield # 接收search傳遞的路徑
with open(abspath,'rb') as f:
target.send((abspath,f)) # send多個用元組的方式,為了把檔案的路徑傳遞下去
#3、循環讀取每一行内容 --for line in f
@init
def cat(target):
while True:
abspath,f=yield #(abspath,f)
for line in f:
res=target.send((abspath,line)) # 同時傳遞檔案路徑和每一行的内容
if res:break
#4、過濾關鍵字 --if 'error' in line
@init
def grep(pattern,target): # # patter是過濾的參數
tag=False #為了去掉重複的檔案路徑,因為一個檔案中存在多個error關鍵字
while True:
abspath,line=yield tag
tag=False
if pattern in line:
target.send(abspath) # 傳遞有相應内容的檔案路徑
tag=True
#5、列印該行屬于的檔案名
@init
def printer():
while True:
abspath=yield
print(abspath)
g = search(opener(cat(grep('os'.encode('utf-8'), printer()))))
g.send(r'F:\Python\Code')
三、函數的遞歸調用
在調用一個函數的過程中,直接或間接的調用了函數本身,就叫遞歸調用
執行有兩個階段:遞推和回溯
遞歸的效率低,需要在進入下一次遞歸時保留目前的狀态,解決方法是尾遞歸,即在函數的最後一步(而非最後一行)調用自己,但是python又沒有尾遞歸,且對遞歸層級做了限制
1. 必須有一個明确的結束條件
2. 每次進入更深一層遞歸時,問題規模相比上次遞歸都應有所減少
3. 遞歸效率不高,遞歸層次過多會導緻棧溢出(在計算機中,函數調用是通過棧(stack)這種資料結構實作的,每當進入一個函數調用,棧就會加一層棧幀,每當函數傳回,棧就會減一層棧幀。由于棧的大小不是無限的,是以,遞歸調用的次數過多,會導緻棧溢出)
#直接
def func():
print('from func')
func()
func()
#間接
def foo():
print('from foo')
bar()
def bar():
print('from bar')
foo()
foo()
# salary(5)=salary(4)+300
# salary(4)=salary(3)+300
# salary(3)=salary(2)+300
# salary(2)=salary(1)+300
# salary(1)=100
#
# salary(n)=salary(n-1)+300 n>1
# salary(1) =100 n=1
def salary(n):
if n == 1:
return 100
return salary(n-1)+300
print(salary(5))
四、子產品
如果你從 Python 解釋器退出再進入,那麼你定義的所有的方法和變量就都消失了。為此 Python 提供了一個辦法,把這些定義存放在檔案中,為一些腳本或者互動式的解釋器執行個體使用,這個檔案被稱為子產品。
子產品是一個包含所有你定義的函數和變量的檔案,其字尾名是.py。子產品可以被别的程式引入,以使用該子產品中的函數等功能。這也是Python标準庫的方法
1 import語句
import加載的子產品分為四個通用類别:
- 使用Python編寫的代碼(.py檔案)
- 已被編譯為共享庫或DLL的C或C++擴充
- 包好一組子產品的包
- 使用C編寫并連結到Python解釋器的内置子產品
import文法:
import module1[, module2[,... moduleN]
當解釋器遇到import語句,如果子產品在目前的搜尋路徑就會被導入,搜尋路徑是一個解釋器會先進行搜尋的所有目錄的清單。如想要導入子產品 support,需要把指令放在腳本的頂端:
support.py 檔案代碼為:
def print_func( par ):
print ("Hello : ", par)
return
test.py引入support子產品:
import support #導入子產品
support.print_func('Yim') #現在可以調用子產品裡包含的函數了
#執行結果
Hello : Yim
還可以給子產品起一個别名:
import support as s1
s1.print_func('Yim')
一個子產品隻會被導入一次,不管你執行了多少次import。這樣可以防止導入子產品被一遍又一遍地執行(可以使用sys.modules檢視驗證):
import support
import support
import support
support.print_func('Yim')
#執行結果:
Hello : Yim
2 from…import*語句
優點:使用源檔案内的名字時無需加字首,使用友善
缺點:容易與目前檔案的名稱空間内的名字混淆
把一個子產品的所有内容全都導入到目前的命名空間也是可行的,隻需使用如下聲明:
from modname import * #不推薦使用
下面隻導入support子產品裡面的print_func函數:
from support import print_func #可以用逗号分隔,寫上多個函數名
print_func('Yim') #直接執行print_func函數
也可以起别名:
from support import print_func as p1
p1('Yim')
可以使用__all__來控制*,修改support.py檔案:
__all__ = [print_func] #可以在清單内添加多個函數名
def print_func( par ):
print ("Hello : ", par)
return
test.py檔案如下:
from support import *
print_func('Yim') #執行結果報錯,NameError: name 'print_func' is not defined
3 子產品的搜尋路徑
搜尋路徑是由一系列目錄名組成的,Python解釋器就依次從這些目錄中去尋找所引入的子產品
搜尋路徑是在Python編譯或安裝的時候确定的,安裝新的庫應該也會修改。搜尋路徑被存儲在sys子產品中的path變量
子產品的查找順序是:記憶體中已經加載的子產品->内置子產品->sys.path路徑中包含的子產品
需要特别注意的是:我們自定義的子產品名不應該與系統内置子產品重名
import sys
print(sys.path)
#執行結果:
['F:\\Python\\Code\\子產品', 'F:\\Python', 'C:\\Python36\\python36.zip', 'C:\\Python36\\DLLs', 'C:\\Python36\\lib', 'C:\\Python36', 'C:\\Python36\\lib\\site-packages']
在腳本中修改sys.path,引入一些不在搜尋路徑中的子產品
import sys
print(sys.path)
sys.path.append('F:\\Python\\Code\\不常用子產品')
# sys.path.insert(0,'F:\\Python\\Code\\不常用子產品') #也可以使用insert,排在前的目錄會優先被搜尋
print(sys.path)
#執行結果
['F:\\Python\\Code\\子產品', 'F:\\Python\\2017-18s', 'C:\\Python36\\python36.zip', 'C:\\Python36\\DLLs', 'C:\\Python36\\lib', 'C:\\Python36', 'C:\\Python36\\lib\\site-packages']
['F:\\Python\\Code\\子產品', 'F:\\Python\\2017-18s', 'C:\\Python36\\python36.zip', 'C:\\Python36\\DLLs', 'C:\\Python36\\lib', 'C:\\Python36', 'C:\\Python36\\lib\\site-packages', 'F:\\Python\\Code\\不常用子產品']
以下是官方文檔:
#官網連結:https://docs.python.org/3/tutorial/modules.html#the-module-search-path
搜尋路徑:
當一個命名為spam的子產品被導入時
解釋器首先會從内模組化塊中尋找該名字
找不到,則去sys.path中找該名字
sys.path從以下位置初始化
1 執行檔案所在的目前目錄
2 PYTHONPATH(包含一系列目錄名,與shell變量PATH文法一樣)
3 依賴安裝時預設指定的
注意:在支援軟連接配接的檔案系統中,執行腳本所在的目錄是在軟連接配接之後被計算的,換句話說,包含軟連接配接的目錄不會被添加到子產品的搜尋路徑中
在初始化後,我們也可以在python程式中修改sys.path,執行檔案所在的路徑預設是sys.path的第一個目錄,在所有标準庫路徑的前面。這意味着,目前目錄是優先于标準庫目錄的,需要強調的是:我們自定義的子產品名不要跟python标準庫的子產品名重複,除非你是故意的
4 __name__屬性
一個子產品被另一個程式第一次引入時,其主程式将運作。如果我們想在子產品被引入時,子產品中的某一程式塊不執行,我們可以用__name__屬性來使該程式塊僅在該子產品自身運作時執行。
if __name__ == '__main__': #檔案當做腳本運作時,__name__ 等于'__main__'
print('程式自身在運作')
else:
print('我來自另一子產品')
#執行結果
程式自身在運作
#互動式,導入子產品
>>> import support
我來自另一子產品
說明: 每個子產品都有一個__name__屬性,當其值是'__main__'時,表明該子產品自身在運作,否則是被引入。
常用執行個體:
def func1():
print('from func1')
def func2():
print('from func2')
if __name__ == '__main__':
func1()
func2()
五、包
包是一種管理 Python 子產品命名空間的形式,采用"點子產品名稱",比如一個子產品的名稱是 A.B, 那麼他表示一個包 A中的子子產品 B
無論是import形式還是from...import形式,凡是在導入語句中(而不是在使用時)遇到帶點的,這都是關于包才有的導入文法
包是目錄級的(檔案夾級),檔案夾是用來組成py檔案(包的本質就是一個包含__init__.py檔案的目錄)
強調:
- 在python3中,即使包下沒有__init__.py檔案,import 包仍然不會報錯,而在python2中,包下一定要有該檔案,否則import 包報錯
- 建立包的目的不是為了運作,而是被導入使用,記住,包隻是子產品的一種形式而已,包即子產品
有一個包結構如下:
glance/ #頂層包
├── __init__.py #初始化 glance 包
├── api
│ ├── __init__.py
│ ├── policy.py
導入包裡的子子產品:
import glance.api.policy
必須使用全名去通路:
glance.api.policy.func()
還有一種導入子產品的方法是:
from glance.api import policy #從子產品中導入一個指定的部分到目前命名空間中
它不需要那些冗長的字首,是以可以這樣使用:
policy.func()
還有一種就是直接導入一個函數或者變量:
from glance.api.policy import func
可以直接使用func()函數:
func()
1 導入包執行個體
包結構如下:
glance/ #頂層包
run.py # 執行檔案
├── __init__.py #初始化 glance 包
├── api
│ ├── __init__.py
│ ├── policy.py
導入包:
#1、run.py用全名通路func(),内容如下:
import glance
glance.api.policy.func()
# glance/__init__.py如下:
import glance.api #也可以用from glance import api
# glance/api/__init__.py如下:
import glance.api.policy #也可以用from glance.api import policy
#2、run.py直接通路func(),内容如下:
import glance
glance.func()
# glance/__init__.py如下:
from glance.api.policy import func #不能用import glance.api.policy.func
# glance/api/__init__.py可以不用寫
當使用from package import item這種形式的時候,對應的item既可以是包裡面的子子產品(子包),或者包裡面定義的其他名稱,比如函數,類或者變量。
import文法會首先把item當作一個包定義的名稱,如果沒找到,再試圖按照一個子產品去導入。如果還沒找到,恭喜,一個:exc:ImportError 異常被抛出了。
反之,如果使用形如import item.subitem.subsubitem這種導入形式,除了最後一項,都必須是包,而最後一項則可以是子產品或者是包,但是不可以是類,函數或者變量的名字。
注意事項:
- 關于包相關的導入語句也分為import和from ... import ...兩種,但是無論哪種,無論在什麼位置,在導入時都必須遵循一個原則:凡是在導入時帶點的,點的左邊都必須是一個包,否則非法。可以帶有一連串的點,如item.subitem.subsubitem,但都必須遵循這個原則
- 對于導入後,在使用時就沒有這種限制了,點的左邊可以是包,子產品,函數,類(它們都可以用點的方式調用自己的屬性)
- 對比import item 和from item import name的應用場景:如果我們想直接使用name那必須使用後者。
2 絕對導入和相對導入
以上面的例子是絕對導入的方式
絕對導入:以glance作為起始
相對導入:用.或者..的方式作為起始(隻能在一個包中使用,不能用于不同目錄内)
執行個體:
run.py直接通路func(),内容如下:
import glance
glance.func()
# glance/__init__.py如下:
from .api.policy import func #1、這樣的好處是:就算包名glance被更改也不會有影響,隻需改run.py内的import …2、..表示上級目錄,可以導入上級目錄中的其他子產品
特别需要注意的是:
- 可以用import導入内置或者第三方子產品(已經在sys.path中),但是要絕對避免使用import來導入自定義包的子子產品(沒有在sys.path中),應該使用from... import ...的絕對或者相對導入
- 包的相對導入隻能用from的形式
六、軟體開發規範

bin:存放執行腳本
conf:存放配置檔案
core:存放核心邏輯
db:存放資料庫檔案
lib:存放自定義的子產品與包
log:存放日志
start.py内容:
import os
import sys
# print(os.path.abspath(__file__)) #擷取目前檔案的絕對路徑,F:\soft\bin\test.py
# print(os.path.dirname(os.path.abspath(__file__))) #擷取目前檔案所在的目錄,F:\soft\bin
# print(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) #擷取目前檔案所在的目錄的上級目錄,F:\soft
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.append(BASE_DIR) #将soft這個包的路徑加到環境變量
from core import core
…… #導入其他子產品
if if __name__ == '__main__':
…… #執行
posted on 2017-08-02 19:36 似水_流年 閱讀( ...) 評論( ...) 編輯 收藏
轉載于:https://www.cnblogs.com/yanmj/p/7275650.html