python函數
1.函數作用
- 最大化代碼重用和最小化代碼冗余
- 流程的分解
2.函數基本概念
- def創建對象并賦值給某一變量
##?創建一個函數對象并賦值給fn_name
def?fn_name(args1,args2,...,argsN):
????<statements>復制代碼
- def是可執行的代碼
##?通過if語句判斷來做定義函數,def是實時執行的
if?test:
????def?fn_name():
????????<statement>
else:
????def?fn_name():
????????<statement>
##?分配函數對象
myFn?=?fn_name
##?調用函數
fn_name()復制代碼
- return將結果對象發送給調用者
##?函數主體一般都包含return語句
def?fn_name(args1,args2,...,argsN):
????...
????return?<value>復制代碼
- lambda創建一個對象并將結果返回
##?生成函數對象的表達形式
lambda?argument1,?argument2,...?argumentN?:?expression?using?arguments
##?lambda注意點
其一,lambda是表達式而不是語句
其二,lambda的主體是一個單個表達式而非語句
##?定義一個正常的函數
def?func(x,y,z):
????return?x+y+z
##?使用lambda表達式
f?=?lambda?x,y,z:x+y+z
##?使用默認參數
f?=?lambda?x=1,y=2,z=3:x+y+z復制代碼
- yield向調用者發回一個結果對象并記住離開的地方
生成器函數
##?編寫常規def語句并用yield語句一次返回一個結果,在每個結果之間掛起并繼續他們的狀態
##?定義生成函數
def?gensquare(N):
????for?index?in?range(N):
????????yield?index?**?2????????
##?等價于以下的函數
def?gensquare(N):
?????yield?0?**?2???????????##?函數每次遇到一個yield便會向調用者發送一個返回值并掛起
?????...
?????yield?(N-1)?**?2
##?yield是發送數據不是返回數據
##?調用生成函數,此時的函數是可迭代,可迭代對象定義了一個__next__方法
for?i?in?gensquare(5):??????
????print(i,end=":")????????
0?:?1?:?4?:?9?:?16?:復制代碼
生成器表達式
##?列表解析表達式
>>>?list?=?[x**2?for?x?in?range(6)]?????
[0,?1,?4,?9,?16,?25]
##?生成器表達式類似上述的列表解析但返回的結果是一個對象而不是一個列表
>>>?genrator?=?(x**2?for?x?in?range(6))
<generator?object?<genexpr>?at?0x1021088e0>
##?執行生成器
>>>?next(my_generator)
0?
>>>?next(my_generator)
1
...
##?編寫一個列表解析器等同于在一個list內置調用中包含一個生成器表達式以迫使其一次生成列表中的所有結果
>>>?my_list?=?list(x**2?for?x?in?range(6))復制代碼
- global聲明函數中模塊級別的變量并進行賦值操作
全局變量
##?全局變量是位于模塊文件內部的頂層的變量名
X?=?80
##?全局變量如果是在函數內被賦值的話,必須經過聲明
def?chang_x():
????##?必須聲明
????global?X
????X?=?90
##?全局變量在函數的內部不經過聲明也可以被引用
def?reference_x():
????print(X)
##?注意:不同的python文件(模塊)之間不要使用『模塊對象.屬性名』對全局變量進行修改,最好的方式通過函數修改
##?a.py
X?=?99
def?change_x(new):
????global?X
????X=?new
##?b.py
import?a
a.change_x(97)
##?訪問全局變量的方式
##?test.py
var?=?99
def?local():?
????var?=?0?????##?外面聲明的var與函數內沒關系,當這個函數執行完畢后,var仍然是99
def?glob1():?
????global?var??##?告知函數中var是屬于全局變量,直接從全局作用域開始查找,若找不到便會到內置作用域查找,如果還找不到將報錯
????var?+=?1????
def?glob2():?
????import?dir1.module??##?dir1與test.py位于同一個目錄下,module是dir1下的一個模塊,var是module下的全局變量
????dir1.module.var?+=?1
def?glob3():?
????import?sys
????glob?=?sys.modules['module']????##?從搜索路徑中獲取模塊,并對該模塊全局變量進行操作
????glob.var?+=?1復制代碼
- nolocal聲明將要賦值的一個封閉的函數變量,即內嵌一個函數
##?基礎語法
def?func():
????nonlocal?name1,?name2,?...?#?OK?here
##?nonlocal名稱只能存在于嵌套的def語句中,不能出現在模塊的全局作用域或def之外的內置作用域
def?tester(start):?
????state?=?start???????????????##?數據保存在tester函數對象之中
????def?nested(label):??????????##?返回內嵌的函數對象并且攜帶了外部函數對象的屬性,每次調用將改變外部函數對象的屬性state
????????nonlocal?state??????????##?使用nonlocal聲明state,state必須是在嵌套函數nested提前定義過
????????print(label,?state)
????????state?+=?1??????????????
????return?nested
>>>?F?=?tester(0)?
>>>?F('spam')?
spam?0
>>>?F('ham')
ham?1
>>>?F('eggs')
eggs?2復制代碼
- 函數參數是通過賦值(對象引用)傳遞的
- 不可變參數通過"值"傳遞
- 可變對象通過"指針"進行傳遞
##?參數傳遞是通過自動將對象賦值給本地變量名來實現的
def?changer(a,b):
????a?=?9???????????????#?a是值傳遞,屬于當前函數的本地變量
????b[-1]?=?"spam"??????#?b是可變對象通過指針傳遞
##?在函數內部的參數名的賦值不會影響調用者
##?改變函數的可變參數的值也許會對調用者有影響
def?changer(a,b,c):
????a?=?9???????????????????##?本地變量的值傳遞不影響調用者
????b[-1]?=?"spam"??????????##?函數改變可變對象所指向的內容值
????c?=?c[:]????????????????##?函數內部拷貝副本,不會對調用者影響
##?阻止可變對象在函數改變內容值
-?使用拷貝
-?轉成不可變對象,如tuple(list)復制代碼
- 參數、返回值以及變量不需要在函數中聲明
##?python函數沒有類型約束,可以傳遞或返回任意類型參數
def?add(a):
????return?a?**?2
>>>?add(3)
9
>>>?add("xiao")
xiaoxiao復制代碼
python賦值參數匹配順序
- 位置:從左至右匹配非關鍵字參數
def?func(a,b,c):
????print?a,b,c
>>>?func(1,2,3)
1,2,3復制代碼
- 關鍵字參數:通過匹配變量名稱分配關鍵字參數,與位置無關
def?func(a,b,c):
????print?a,b,c
>>>?func(c=3,a=2,b=1)
2,1,3復制代碼
- 其他額外的非關鍵字參數分配到*name元組中
##?任意非關鍵字參數
def?func(*args):
????print(args)?????##?傳遞進來是元組數據并賦值變量名稱為args
##?調用
>>>?f1(29,34,4,3,12,13)
29,34,4,3,12,13,復制代碼
- 其他額外的關鍵字參數分配到**name字典中
##?任意關鍵字參數
def?func(**args):
????for?key,value?in?args.items():
????????print(key?+"-->"?+?value)
##?調用
>>>?f2(name="xiaoxiao",url="https://www.baidu.com")
url--https://www.baidu.com
name--xiaoxiao復制代碼
- 使用默認值分配給在頭部未得到分配的參數
##?函數定義默認參數值
##?以下函數在定義參數傳遞的時候就已經錯誤,自然調用就失敗
def?fn(name="xiao",age):
????print("the?name?is?"+name+",and?the?age?is?"+age)
>>>?fn(34)??????##?調用失敗
SyntaxError:?non-default?argument?follows?default?argument
>>>?fn(age=34)???##?調用失敗
SyntaxError:?non-default?argument?follows?default?argument
##?正常的定義方式是沒有指定默認參數值在前,有默認參數值的定義在后
def?fn(age,name="xiao"):
????print("the?name?is?"+name+",and?the?age?is?"+age)
>>>?fn(34)??????????##?調用正常
>>>?fn(age=34)??????##?調用正常復制代碼
python模塊與包
1.模塊
模塊組成
- import:使導入者以一個整體獲取模塊
- from:允許客戶端從一個模塊中獲取特定的變量名
- imp.reload:在中止py程序中,提供一種重新載入模塊文件代碼的方法
模塊扮演的角色
- 代碼重用
- 系統命名空間的劃分
- 實現共享服務和數據
import在模塊第一次導入時執行三個步驟
- 找到模塊文件,即搜索模塊
- 編譯成位碼
- 執行模塊的代碼來創建所定義的對象
sys.path:即模塊搜索路徑
- 程序的主目錄
- PYTHONPATH目錄
- 標準鏈接庫目錄
- 任何.pth文件的內容
模塊編寫
- import將整個模塊對象賦值給一個變量名
- from將一個或多個變量名賦值給另一個模塊中同名的對象
##?相同主目錄
##?module1.py???
def?check(num):
????return?num>0
##?module2.py
import?module1
module1.check(9)
##?from:把模塊文件中的一個或者多個變量名從中復制到當前引用的作用域內,此時無需再通過模塊調用
from?module1?import?check
check(9)
##?from?*:把模塊文件中所有定義好的變量名復制到當前引用的作用域中
from?moudle1?import?*
check(9)復制代碼
from與import對等性
from module import name1,name2
等效于
import module
name1 = module.name1
name2 = moudle.name2
del module復制代碼
模塊文件生成命名空間
- 模塊語句在首次導入時執行
- 頂層的賦值語句會創建模塊屬性
- 模塊的命名空間能通過屬性
__dict__
或dir(module)獲取 - 模塊是一個獨立作用域(本地變量就是全局變量)
重載模塊:python內置函數reload()
- reload會在模塊當前命名空間內執行模塊文件的新代碼
- 文件中頂層賦值語句會使得變量名換成新值
- 重載會影響所有使用import讀取模塊的客戶端
- 重載只會對以后使用from的客戶端造成影響
##?使用reload()的時候,模塊是預先加載過的
/main
????/dir1
????????__init__.py
????????/dir2
????????????__init__.py
????????????dir2module.py
????test.py
##?main的主目錄加載到搜索路徑中
>>>?import?dir1.dir2.dir2module
dir1?init.....
dir2?init....
dir2?module?py?...
>>>?reload(dir1.dir2.dir2module)????##?重新加載dir2module,而不會重新加載dir1和dir2的初始化操作
dir2?module?py?...
##?重新加載dir1和dir2
>>>?reload(dir1)?
>>>?reload(dir1.dir2)復制代碼
2.包
包的導入
- 每一個python模塊包都必須含有
__init__.py
文件 - 增加主目錄到包的搜索路徑中,即PYTHONPATH或者是.pth文件中
- 模塊搜索路徑的項目提供平臺特定的目錄路徑前綴,之后再在import的路徑左邊添加這些路徑
包的執行
- 包的初始化:導入某個目錄時,會自動執行改目錄下
__init__.py
文件中的所有程序代碼 - 模塊命名空間的初始化:導入后會變成真實的嵌套對象路徑
- from 語句的行為:可以在
__init__.py
定義目錄以from 語句形式導入時,需要導出什么
##?當前目錄結構:
dir0
????/dir1
????????__init__.py
????????a.py
????????/dir2
????????????__init__.py
????????????b.py
????/module2
????????__init__.py
????????????/module3
????????????????__init__.py
????????????????b.py
????test.py?????????
dir0稱為主目錄(__init__.py可有可無),dir1?和?dir2?是模塊包,將主目錄添加到搜索路徑中
##?常規導入
>>>?import?dir1.dir2.b??????##?導入后會運行并返回一個模塊對象
dir1?init.....??????????????##?dir1下的__init__.py
dir2?init....???????????????##?dir2下的__init__.py
dir2?module?py?...??????????##?dir2下的b.py
##?使用from導入
>>>?from?dir1.dir2?import?b?????##??避免每次讀取時重新輸入路徑
dir1?init.....??????????????##?dir1下的__init__.py
dir2?init....???????????????##?dir2下的__init__.py
dir2?module?py?...??????????##?dir2下的b.py復制代碼
相對包導入作用域
- 相對導入適用于只在包內導入
- 相對導入只是用于from語句
- 術語含糊不清
##?可以使用from語句前面的點號(".")來指定,導入相對于外圍的包,
##?這樣的導入只是在包內部搜索而非在搜索路徑(sys.path)搜索
##?目錄結構
dir1
????dir2
????????__init__.py
????????a.py
????test.py
##?test.py下
from?.dir2?import?a??##?和test.py相同包路徑下的dir2文件夾的a模塊的導入復制代碼
模塊查找總結
- 簡單模塊通過搜索sys.path路徑列表上每個目錄查找,從左至右
- 包是帶有一個特殊的
__init__.py
文件的Python模塊的直接目錄,可以使用A,B,C目錄路徑語法導入 - 同一個包文件中,常規的import語句使用將會通過sys.paths規則搜索,而包中的導入使用from語句以及前面的點號,只是檢查包目錄