1、函數
函數的做用:能夠計算出一個返回值,最大化代碼重用,最小化代碼冗餘,流程的分解。python
一、函數相關的語句和表達式
語句 例子
Calls myfunc(‘diege','eggs',meat=‘lit’) #使用函數
def,return,yield def adder(a,b=1,*c):
return a+b+c[0]
global changer():
global x;x='new'
lambda Funcs=[lambad x:x**2,lambad x:x*3]sql
二、編寫函數
def建立了一個對象並將其賦值給某一個變量名。
return將一個結果對象發送給調用者。
函數是經過賦值(對象引用)傳遞的。
參數經過賦值傳遞給函數。
global聲明瞭一個模塊級的變量並被賦值。
參數,返回值以及變量並非聲明
def <name>(arg1,age2,...,agrN):
<statements>
return <value
函數主體每每都包含了一個return語句(不是必須的),若是它沒有出現,那麼函數將會在控制流執行完函數主體時結束,技術上,沒有返回值的函數自動返回了none對象。
return能夠在函數主體中的任何地方出現。它表示函數調用的結束,並將結果返回至函數調用。
不要嘗試判斷傳入參數的對象類型,這樣實質上破壞了函數的靈活性,把函數限制在特定的類型上
數據庫
三、做用域
做用域:變量定義以及查找的地方。
本地變量:在函數內定義的變量爲本地變量。
全局變量:在文件內[模塊內]函數外定義的變量爲全局變量
Python建立,改變或者查找變量名都是在所謂的命名空間(一個保存變量名的地方,模塊中爲__main__)中進行。做用域這個術語指的就是命名空間。
也就說,在代碼中變量名被賦值的位置決定了這個變量名能被訪問到的範圍
一個函數全部變量名都與函數的命名空間相關聯。
*def內定義變量名def內使用,是本地變量
*def之中的變量名與def以外的變量名不發生衝突,使用別處相同的變量名也沒問題。
函數定義了本地做用域,而模塊定義了全局做用域。兩做用域關係。
*內嵌的模塊是全局做用域:對於外部的全局變量就成爲了一個模塊對象的屬性
*全局做用域的做用範圍僅限單個文件:不要被全局迷惑,這裏的全局是一個文件的頂層的變量名,僅對這個文件內部的代碼而言是全局。
Python中,沒有一個無所不包的情景文件做用域。替代的方法是,變量名由模塊文件隔開,必須精準地導入一個模塊文件纔可以使用這文件中
定義的變量名,
*每次對函數的調用都建立了一個新的本地做用域。
*賦值的變量名廢除聲明爲全局變量,不然均爲本地變量。
*所用的變量名均可以概括爲本地,全局,或者內置。(內置:ptyhon預約義的__builtin__模塊提供的)
變量名解析:LEGB原則
對一個def語句
*變量名引用分爲三個做用域進行查找:首先查找本地,而後是函數內(若是有),以後全局,最後內置。
*默認狀況下,變量名賦值會建立或改變本地變量
*全局聲明將賦值變量名映射到模塊文件內部的做用域。
global
global語句包含關鍵字global
*全局變量是位於模塊文件內部頂層的變量名
*全局變量若是是在函數內部被賦值的話,並需通過聲明
*全局變量名在函數的內部不通過聲明也能夠被引用
四、傳遞參數
參數傳遞:傳遞給函數做爲其輸入對象的方式
*參數的傳遞是經過自動將對象賦值給本地變量來實現的。
*在函數內部的參數名的賦值不會影響調用者。
*改變函數的可變對象參數的值也許會對調用者有影響。
換句話說,由於參數是簡單的經過賦值進行對象的傳遞,函數可以改變傳入的可變對象,所以其結果會影響調用者。
*不可變參數是「經過值」進行傳遞。
像整數和字符串這樣的對象是經過對象引用而不是拷貝進行傳遞的,可是由於你不管如何都不可能在原處改變不可變對象,實際的效果就是很像建立了一份拷貝。
可變對象是經過「指針」進行傳遞的。
避免可變參數的修改
在Python中,默認經過引用(也就是指針)進行函數的參數傳遞。若是不想在函數內部在原處的修改影響傳遞給它的對象。那麼,可以簡單的建立一個可變對象的拷貝。
咱們老是可以在調用時對列表進行拷貝
L=[1,2]
changer(X,L[:])
若是不想改變傳入的對象,不管函數是如何調用的,咱們能夠在函數內部進行拷貝,避免可變參數的修改
>>> def changer(a,b):
... a=2
... b=b[:]
... b[0]='diege'
特定參數匹配模型
參數在ptyhon中老是經過賦值進行傳遞,傳入的對象賦值給了在def頭部的變量名。儘管這樣,在模型的上層,python提供了額外的工具,該工具改變了調用過中
賦值時參數對象匹配在頭部的參數名的優先級。這些工具是可選的。
總結與特定模式有關的語法:
語法 位置 解釋
func(value) 調用者 常規參數,經過位置進行匹配,從左到右進行匹配
func(name=value) 調用者 關鍵字參數,經過變量名匹配
func(*name) 調用者 以name傳遞全部的對象,並做爲獨立的基於位置的參數
func(**name) 調用者 以name成對的傳遞全部的關鍵字/值,並做爲獨立的關鍵字的參數
def func(name) 函數 常規參數:經過位置或變量名進行匹配
def func(name=value) 函數 默認參數值:若是沒有在調用中傳遞的話,就是用默認值
def func(*name) 函數 匹配並收集(在元組中)全部包含位置的參數
def func(**name) 函數 匹配並收集(在字典中)全部包含位置的參數。
五、匿名函數:lamdba
lambad 建立了一個以後可以被調用的函數,它返回了一個函數而不是將這個函數賦值給一個變量名。
lambda表達式
lanbda arg1,arg2,...,argN:expression using arguments
lambda 是一個表達式,而不是一個語句
lambda的主體是一個單個的表達式,而不是代碼塊
func(x,y,z):return x+y+z
默認參數也可以再lambda參數中使用,就像在def中使用同樣。
lambda a='free',b='file',c='feel':a+b+c
六、做參數來應用函數
內置函數apply
當須要變得更加動態的話,能夠經過將一個函數做爲一個參數傳遞給apply來調用一個生成的函數,而且
也將傳給那個函數的參數做爲一個元組傳遞給apply函數()
apply(函數,參數1(元組),參數2(元組))
七、在序列中映射函數:map
使用內置工具map,map函數會對一個序列對象中的每個元素應用被傳入的函數,而且返回一個包含了全部函數調用結果的一個列表。
map(函數,傳入函數的序列對象)
>>> def inc(x):return x+10
>>> L=[1,2,3,4,5]
>>> map(inc,L)
[11, 12, 13, 14, 15]
>>> L=[1,2,3,4,5]
map嵌套lambda
>>> map((lambda x:x+3),L)
[4, 5, 6, 7, 8]
高級功能:提供了多個序列做爲參數,它可以並行返回分別以每一個序列的元素做爲【函數對應參數】獲得的結果的列表
>>> pow(3,4)
81
>>> map(pow,[1,2,3],[2,3,4]) #1**2,2**3,3**4
[1, 8, 81]
八、函數式編程工具:filter和reduce
函數式編程的意思就是對序列應用一些函數的工具。
基於某一測試函數過濾出一些元素-filter
對每對元素都應用函數並運行到最後結果-reduce
>>> filter((lambda x:x>0),range(-5,5))
[1, 2, 3, 4]
這個等效於for range:if語句
reduce稍微複雜一點。這裏兩個reduce調用,計算在一個列表中全部元素加起來和以及乘起來的乘積
>>> reduce((lambda x,y:x+y),[1,2,3,4])
10
>>> reduce((lambda x,y:x*y),[1,2,3,4])
24
依次計算
九、重訪列表解析:映射
1)、列表解析基礎
>>> [x**2 for x in range(10)]
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
>>> map((lambda x:x**2),range(10))
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
2)、增長測試和嵌套循環
在for以後編寫if分支,用來增長邏輯選擇,if分支至關filter
>>> [x for x in range(5) if x%2==0]
[0, 2, 4]
>>> filter((lambda x:x%2==0),range(5))
[0, 2, 4]
3)filter出來的列表能夠做爲map的第2個參數
>>> map((lambda x:x**2),filter((lambda x:x%2==0),range(5)))
[0, 4, 16]
列表解析和map的對比
二者相似,都會收集對序列或其餘可迭代對象中每一個元素應用元算後的結果(一次一個項目),從而建立新列表。其主要差別在於。
map會對每一個元素應用函數,而列表解析則是應用任意的表達式。所以,列表解析更通用一些,能夠像map那樣應用函數調用表達式,
可是,map須要一個函數才能應用其餘種類的表達式(函數是map的第一參數).列表解析也支持擴展語法,若是,嵌套for循環和if
分句從而能夠包含內置函數filter的功能。
十、重返迭代器:生成器
編寫的函數可以返回一個值,而且稍後還能夠從它剛纔離開的地方仍然返回值。這樣的函數被認做是生成器,由於它們隨時間生成一個序列的值。
大多數方面生成器函數就像通常函數,在Python它們被自動用做實現迭代協議,因它只可以再迭代的語境中出現。
生成器和通常的函數之間代碼上最大的不一樣就是一個生成器yield一個值,而不是return一個值。yield語句將會將函數關起,並向它的調用者返回一個值
可是保存足夠的狀態信息爲了讓其可以在函數從它掛起的地方恢復。
包含yield的語句的函數將會特意編譯成爲生成器。當調用它時,他們返回一個生成器對象,這個生成器對象支持迭代器對象接口。
>>> def Dtest(N):
... for i in range(N):
... yield i**2
使用
>>> for i in Dtest(5):
... print i,':',
...
0 : 1 : 4 : 9 : 16 :
查看過程
>>> x=Dtest(4)
>>> x.next()
0
>>> x.next()
十一、函數設計概念
*耦合性:對於輸入使用參數,而且對於輸出使用return語句
*耦合性:只有在真正必要的狀況下使用全局變量。
*耦合性:不要改變可變類型的參數,除非調用者但願這樣作。
*聚合性:每個函數都應該有一個單一的,同一的目標
*大小:每個函數應該相對較小。
*耦合:避免直接改變在另外一個模塊文件中的變量。
函數是對象:簡潔調用
2、模塊
一、基本
每一個文件都是一個模塊,而且模塊導入其餘模塊以後就可使用導入模塊定義的變量名。模塊能夠由兩個語句和一個重要的內置函數進行處理。
import: 使客戶端(導入者)以一個總體獲取一個模塊。
from:允許客戶端從一個模塊文件中獲取特定的變量名。
reload:在不停止Python程序的狀況下,提供了一個從新載入模塊文件代碼的方法。
import和from是賦值語句,是可執行的語句,能夠嵌套到if ,def語句中和def同樣import和from都是隱性賦值語句
在一個模塊文件的頂層定義的全部變量名都成爲了被導入的模塊對象的屬性。
模塊至少有三個角色:
代碼重用:模塊仍是定義變量名的空間,被認做是屬性。能夠被多個外部的客戶端應用。
系統命名空間的劃分:
現實共享服務和數據:
二、python程序構架
import如何工做
執行三個步驟
1)、找到模塊文件
2)、編譯成位碼(須要時)
3)、執行模塊的代碼來建立其所定義的對象。
在以後導入相同的模塊時候,會跳過這三個步驟,而只提取內存中已加載模塊對象。
express
搜索模塊
導入模塊時,不帶模塊的後綴名,好比.pyapache
Python搜索模塊的路徑:
1)、程序的主目錄
2)、PTYHONPATH目錄(若是已經進行了設置)
3)、標準鏈接庫目錄(通常在/usr/local/lib/python2.X/)
4)、任何的.pth文件的內容(若是存在的話).新功能,容許用戶把有效果的目錄添加到模塊搜索路徑中去
.pth後綴的文本文件中一行一行的地列出目錄。
這四個組建組合起來就變成了sys.path了,
>>> import sys
>>> sys.path
導入時,Python會自動由左到右搜索這個列表中每一個目錄。
第1,第3元素是自動定義的,第2,第4能夠用於擴展路徑,從而包括本身的源碼目錄。
三、模塊的建立和使用。
建立模塊
後綴.py文本文件,模塊頂層指定的全部變量名都會變成其屬性。
定義一個module.py模塊
name='diege'
age=18
def printer(x):
print x
使用模塊
import所有導入,import將整個模塊對象賦值給一個變量名,模塊只導入一次,由於該操做開銷大
>>> import module
屬性
>>> module.name
'diege'
函數
>>> module.printer('hi')
hi
from語句:from 將獲取(複製)模塊特定變量名
from會把變量名賦值到另外一個做用域,因此它就可讓咱們直接在腳本中使用複製後的變量名,而不是經過模塊
from 模塊名 import 須要複製的屬性 將一個或多個變量名賦值給另外一個模塊中同名的對象
from 模塊名 import 須要複製的屬性 as 新的屬性名 將一個或者多個變量名賦值給另外一個模塊中不一樣名的對象
from * 語句 from 模塊名 import * 取得模塊頂層全部賦了值的變量名的拷貝
>>> from module import name
>>> name
'diege
>>> from module import name as myname
>>> myname
'diege'
>>> from module import printer as PR
>>> PR('hi python')
hi python
>>> from module import name,age 複製多個變量名時要用逗號隔開
>>> name,age
('diege', 18)
>>> from module import name as myname,age as myage 複製多個變量名並改變需時須要用逗號隔開多個as
>>> from module import *
四、模塊命名空間
模塊最好理解爲變量名的封裝,簡而言之,模塊就是命名空間(變量名創建所在的場所),而存在於模塊以內的變量名就是模塊對象的屬性。
文件生成命名空間
*模塊語句會在首次導入時執行。
*頂層的賦值語句會建立模塊屬性(文件頂層不在的def和class以內的,但def和class隱性建立的變量名也屬於模塊屬性)。賦值的變量名會存儲在模塊的命名空間內。
*模塊的命名空間能經過屬性__dict__(module.__dict__)或dir(module)獲取
因爲導入而創建的模塊的命名空間是字典,可經過模塊對象相關聯的內置__dict__屬性讀取。
dir函數查看,大至與對象的__dict__屬性的鍵排序後的列表相等,可是它還包含了類繼承的變量名。
*模塊是一個獨立的做用域。
五、重載模塊
模塊程序代碼默認值對每一個過程執行一次,要強制使模塊代碼從新載入並從新運算須要使用reload內置函數。
reload是函數,import是語句。兩個語法不同。
>>> import module
>>> reload(module)
<module 'module' from 'module.pyc'>
>>> reload(test17)
<module 'test17' from '/root/test17.py'>
reload()以前需得import過一次
六、模塊包
除模塊名之外,導入也能夠指定目錄路徑,Pytyh代碼的目錄就是稱爲包。所以這類導入就稱爲包導入
import dir1.dir2.mod
from dir1.dir2.mod import x
.號路徑至關於機器上目錄層次的路徑。
dir1在容器目錄dir0中,dir0這個目錄能夠在Python模塊搜索路徑中找到。
__init__.py包文件
若是選擇使用包導入,那就必須遵循一條約束:包導入語句的路徑的每一個目錄內部都必須有__init__.py這個文件,
不然導入包會失敗。
dir1和dir2中必須包含__init__.py,容器目錄dir0不須要這類文件。由於自己沒有列在import語句中
__init__.py文件能夠包含程序代碼,也能夠是空的。 更一般狀況下,__init__.py文件扮演了包初始化的掛鉤
替目錄產生模塊命名空間以及用目錄導入實現from *行爲的角色。
*包初始化:
首次導入某個目錄時,會自動執行該目錄下__init__.py文件中全部程序代碼。
因此這個文件就是放置包內文件所需初始化的代碼的場所。可使用其初始化文件,建立所需的數據文件,
鏈接數據庫等。
*模塊命名空間的初始化:
*from * 語句的行爲:
做爲一個高級功能,能夠在__init__.py文件中使用__all__列表來定義目錄以form *語句形式導入時,須要
導出什麼。__all__列表是指出當包(目錄—)名稱使用from *的時候,應該導入的子模塊名稱清單。
eg:
/usr/local/lib/python2.7/sqlite3/__init__.py
from dbapi2 import *
/usr/local/lib/python2.7/site-packages/mod_python/__init__.py
__all__ = ["apache", "cgihandler", "psp",
"publisher", "util", "python22"]
version = "3.3.1"
常見的第三方擴展都是以包目錄形式發佈給用戶,而不是單純的模塊列表。
這樣就能夠經過路徑來導入
七、在模塊中隱藏數據
最小化from *的破壞:_X和__all__達到隱藏變量名的目的
有種特定狀況,能夠把下劃線放在變量名前(_X),能夠防止客戶端使用from * 語句導入模塊名時,把其中的那些變量名賦值出去。這實際上是爲了把命名空間的破壞最小化而已。下劃線和__all__不是私有聲明,還能夠經過其餘導入形式 修改這類變量名。例如import語句、from module import _X
之外,也能夠在模塊頂層把變量名的字符串列表賦值給變量__all__,以達到相似於_X命名慣例的隱藏效果【__all__是不隱藏的】
mod_python.__all__ 能夠看到能夠用from *語句複製那些變量名
_X和__all__ 對比
_X 隱藏了 沒法from *
__all__ 只顯示,from *只能獲取__all__中指定的,其餘隱藏。
python中from *會先尋找模塊內的__all__列表,有的話複製其中的變量名,若是沒有定義的話,from *就會複製開頭沒有下劃線的全部命令名。
怎麼以爲__all__列表裏存放的是模塊呢???
八、混合用法模式:__name__和__main__
這是一個特殊的與模塊相關的技巧,能夠把文件做爲模塊導入,並以獨立式程序的形式運行。每一個模塊都有個名爲__name__的內置屬性。Python會自動設置該屬性:
*若是文件是以頂層程序文件執行,在啓動時,__name__就會被設置爲字符串__main__
*若是文件被導入,__name__就會改設成客戶端所瞭解模塊名。
結果就是模塊能夠檢測本身的__name__,來肯定它是執行仍是在導入。
定義一個文件test15.py
def tester():
print "It's python test!"
if __name__=='__main__':
tester()
九、修改模塊搜索路徑
能夠經過PYTHONPATH以及可能的.pth路徑文件進行定製。
Python程序自己是修改sys.path的內置列表。sys.path在程序啓動時就進行初始化,但那以後也能夠隨意對其元素進行刪除,附加和重設
>>> import sys
>>> sys.path
# cd /tmp/
# python
>>> sys.path.append('/root')【增長新的路徑】
>>> sys.path
['', '/usr/local/lib/python2.7/site-packages/MySQL_python-1.2.3-py2.7-freebsd-8.2-RELEASE-i386.egg', '/usr/local/lib/python2.7/site-packages/setuptools-0.6c12dev_r88846-py2.7.egg', '/usr/local/lib/python2.7/site-packages/Babel-0.9.6-py2.7.egg', '/usr/local/lib/python2.7/site-packages/Trac-0.12.3-py2.7.egg', '/usr/local/lib/python2.7/site-packages/Genshi-0.6-py2.7.egg', '/usr/local/lib/python2.7/site-packages/IniAdmin-0.2_r10454-py2.7.egg', '/usr/local/lib/python2.7/site-packages/TracAccountManager-0.4dev_r11251-py2.7.egg', '/usr/local/lib/python2.7/site-packages/SvnAuthzAdminPlugin-0.2-py2.7.egg', '/usr/local/lib/python27.zip', '/usr/local/lib/python2.7', '/usr/local/lib/python2.7/plat-freebsd8', '/usr/local/lib/python2.7/lib-tk', '/usr/local/lib/python2.7/lib-old', '/usr/local/lib/python2.7/lib-dynload', '/usr/local/lib/python2.7/site-packages', '/root']
導入/root目錄下test17.py,注意啓動python時在/tmp目錄,因此/root不是程序啓動目錄
>>> import test17
>>> dir(test17)
>>> test17.lessthan(3,4)
True
十、相對導入語法
from語句如今可使用點號(.)更傾向於同一個包內的模塊(稱爲包相對導入),而不是位於模塊導入搜索路徑上其餘地方的模塊(所謂的絕對導入)
*如今,可使用點號指出該導入應該與其所在包相關聯:這類導入傾向於導入位於該包內的模塊,而不是導入搜索路徑sys.path上其餘地方的同名模塊
from .apache CallBack as CB
同一個包內導入apache模塊CallBack爲CB變量
十一、模塊設計理念
*老是在Python的模塊內編寫代碼
*模塊耦合要降到最底:全局變量。模塊應該儘量和其餘模塊的全局變量無關。
*最大化模塊的沾合性:統一目標
*模塊應該少去修改其餘模塊的的變量。
模塊是對象:元程序
模塊經過內置屬性顯示了他們大多數的特性。所以,可很容易的編寫程序來管理其餘程序。咱們一般稱這類管理程序爲元程序,由於他們是在其餘系統上工做。這也 稱爲內省,由於程序能看見和處理對象的內部。內省是高級功能,可是它能夠作建立程序工具,取得模塊內名爲name的屬性,方法包括
(1)可使用結合點號運算,
(2)或者對模塊的屬性字典進行索引運算(在內置__dict__屬性中顯示)。
(3)Python也在sys.modules字典中導出全部已經加載的模塊。
(4)並提供一個內置函數getattrr,讓咱們以字符串名來取出屬性。(就好像object.attr,而attr是運行時的字符串)
>>> test17.name
'diege'
>>> test17.__dict__.keys()
>>> test17.__dict__['name']
'diege
>>> test17.__dict__['lessthan']
<function lessthan at 0x28495844>
>>> sys.modules 顯示全部加載的模塊
>>> sys.modules['test17']
<module 'test17' from '/root/test17.py'>
>>> sys.modules['test17'].name
'diege
>>> getattr(test17,'lessthan')
<function lessthan at 0x28495bc4>
十二、模塊陷阱
1)頂層代碼的語句次序的重要性
*在導入時,模塊文件頂層的程序代碼(不在函數內)一旦python運行,就會馬上執行。所以,該語句是沒法引用文件後面位置賦值的變量名。
*位於函數主體內的代碼知道函數被調用後纔會運行。
做爲一條原則,若是須要把當即執行的代碼和def一塊兒混用,就要把def放在文件前面,把頂層代碼放在後面。這樣的話,你的函數在使用的代碼運行時,能夠保證他們都已定義並賦值過了。
2)經過變量名字符串導入模塊的方法
import或from語句內的模塊名是」硬編碼「的變量名。
>>> x='string'
>>> import x
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ImportError: No module named x
這裏python會試着導入文件x.py
爲了不發生這樣的問題,一般的作法就是把import語句構形成python代碼的字符串,再傳給exec語句執行:
>>> modname='string'
>>> exec "import "+modname
exec語句以及和他相似eval會編譯一段字符串代碼,將其傳給Python解析器來執行。
3)from複製變量名,而不是鏈接
from語句實際上是在導入者的做用域內對變量名的賦值語句,也就是變量名拷貝運算,而不是變量名的別名機制。它的實現和python全部賦值運算都同樣, 微妙之處在於,共享對象的代碼存在於不一樣的文件中。而後,咱們使用import得到了整個模塊,而後賦值某個點號運算的變量名,就會修改導入的模塊中的變 量名。點號運算把python定向到了模塊對象,而不是賦值模塊中對象。
4)from*會讓變量語義模糊
5)reload不會影響from導入
6)不要使用reload、from以及交互模式測試
reload中引用模塊得經過import至少將其加載一次:
不要from導入以後reload
7) reload使用沒有傳遞性
當重載一個模塊時,Python只會重載那個模塊的文件,不會自動重載該文件重載嘶碰巧還要導入的模塊。
8)遞歸形式的from import沒法工做
不要在遞歸導入中使用 from。編程