函數高級應用
變量作用域
全局變量
標識符的作用域是定義爲其聲明在程序裏的可應用範圍,也就是變量的可見性
在一個模塊中最高級別的變量有全局作用域
全局變量的一個特徵是除非被刪除掉,否則它們的存活到腳本運行結束,且對於所有的函數,他們的值都是可以被訪問的
局部變量
局部變量只是暫時地存在,僅僅只依賴於定義他們的函數現階段是否處於活動
當一個函數調用出現時,某局部變量就進入聲明他們的作用域,在那一刻,一個新的局部變量名爲那個對象創建了
一旦函數完成,框架被釋放,變量將會離開作用域
如果局部與全局有相同名稱的變量,那麼函數運行時,局部變量的名稱將會把全局變量名稱遮蓋住
>>> x = 4
>>> def foo():
... x = 10
... print "in foo,x = ",x
...
>>> foo()
in foo,x = 10
>>> print "in main,x =",x
in main,x = 4
global語句
>>> x = 10
>>> def foo():
... global x
... x = 30
...
>>> x
10
>>> foo()
>>> x
30
注意事項:列表、字典可變對象,全局變量會被修改。如下所示。不可變對象,全局變量不會修改
>>> alist = [10]
>>> def foo():
... alist.append(20)
...
>>> foo()
>>> alist
[10, 20]
>>> def bar(adict):
... adict['name'] = 'bob'
...
>>> bdict = {}
>>> bar(bdict)
>>> bdict
{'name': 'bob'}
>>> def foobar(astr):
... astr = 'hello'
...
>>> bstr = ''
>>> foobar(bstr)
>>> bstr
''
名字空間
任何時候,總有一個到三個活動的作用域(內建、全局和局部)
標識符的搜索順序依次是局部、全局和內建
提到名字空間,可以想象是否有這個標識符
提到變量作用域,可以想象是否可以“看見”這個標識符
函數式編程
偏函數
偏函數的概念是將函數式編程的概念和默認參數以及可變參數結合在一起
一個帶有多個參數的函數,如果其中某些參數基本上固定的,那麼就可以通過偏函數爲這些參數賦默認值
>>> from operator import add
>>> from functools import partial
>>> add10 = partial(add,10)
>>> print add10(25)
35
偏函數實例列舉:
vim -o u2d.py d2u.py
2 files to edit
[root@sail qh1]# vim -O u2d.py d2u.py
2 files to edit
[root@sail qh1]# cat u2d.py
#!/usr/bin/env python
import sys
def unix2dos(fname,sep='\r\n'):
dst_name = fname + '.txt'
with open(fname) as src_fobj:
with open(dst_name,'w') as dst_fobj:
for line in src_fobj:
dst_fobj.write("%s%s" % (line.rstrip('\r\n'),sep))
if __name__ == '__main__':
unix2dos(sys.argv[1])
[root@sail qh1]# cat d2u.py
#!/usr/bin/env python
import sys
import u2d
from functools import partial
dos2unix = partial(u2d.unix2dos,seq='\n')
if __name__ == '__main__':
dos2unix(sys.argv[1])
簡單GUI類的例子
編寫一個窗口程序,要求如下
1、窗口程序提供三個按鈕
2、其中兩個按鈕的前景色均爲白色,背景色爲藍色
3、第三個按鈕前景色爲紅色,背景色爲紅色
4、按下第三個按鈕後,程序退出
#!/usr/bin/env python
import Tkinter
from functools import partial
root = Tkinter.tk()
MyButton = partial(Tkinter.Button,root,fg='white',bg='blue')
b1 = MyButton(text='Button1')
b2 = MyButton(text='Button2')
qb = MyButton(text='quit',command = root.quit)
b1.pack()
b2.pack()
qb.pack()
Tkinter.mainloop()
遞歸函數
如果函數包含了對其自身的調用,該函數就是遞歸的
在操作系統中,查看某一目錄內所有文件、修改權限等都是遞歸的應用
#!/usr/bin/env python
def func(num):
if num == 1:
return num
return num * func(num - 1)
if __name__ == '__main__':
print func(5)
print func(6)
$ mkdir -p demo/{aaa/bbb,ccc}
$ cp /etc/hosts /home/qihang/demo/
$ touch /home/qihang/demo/d.txt
$ touch /home/qihang/demo/aaa/{a1,a2}.txt
$ touch /home/qihang/demo/aaa/bbb/b.txt
$ touch /home/qihang/demo/ccc/{c1,c2}.txt
$ ls -R /home/qihang/demo/
/home/qihang/demo/:
? aaa ccc d.txt hosts
/home/qihang/demo/aaa:
a1.txt a2.txt bbb
/home/qihang/demo/aaa/bbb:
b.txt
/home/qihang/demo/ccc:
c1.txt c2.txt
編寫程序實現ls -R /home/qihang/demo/的功能
#!/usr/bin/env python
import os
import sys
def lsdir(folder):
contents = os.listdir(folder)
print "%s:\n%s\n" %(folder,contents)
for item in contents:
full_path = os.path.join(folder,item)
if os.path.isdir(full_path):
lsdir(full_path)
if __name__ == '__main__':
lsdir(sys.argv[1])
os模塊有這個遞歸的函數
>>> os.walk('/home/qihang/demo/')
<generator object walk at 0xb707d2d4>
>>> list(os.walk('/home/qihang/demo/'))
[('/home/qihang/demo/', ['aaa', 'ccc'], ['hosts', 'd.txt']), ('/home/qihang/demo/aaa', ['bbb'], ['a1.txt', 'a2.txt']), ('/home/qihang/demo/aaa/bbb', [], ['b.txt']), ('/home/qihang/demo/ccc', [], ['c2.txt', 'c1.txt'])]
>>> a = os.walk('/home/qihang/demo/')
>>> a.next()
('/home/qihang/demo/', ['aaa', 'ccc'], ['hosts', 'd.txt'])
>>>
>>> a.next()
('/home/qihang/demo/aaa', ['bbb'], ['a1.txt', 'a2.txt'])
>>> a.next()
('/home/qihang/demo/aaa/bbb', [], ['b.txt'])
>>> a.next()
('/home/qihang/demo/ccc', [], ['c2.txt', 'c1.txt'])
>>> a.next()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
#!/usr/bin/env python
import os
import sys
def lsdir(folder):
for path,dirs,files in os.walk(folder):
print '%s:\n%s\n' % (path,(dirs + files))
if __name__ == '__main__':
lsdir(sys.argv[1])
內部函數
閉包
閉包將內部函數自己的代碼和作用域以及外部函數的作用結合起來
閉包的詞法變量不屬於全局名字空間域或者局部的--而屬於其他的名字空間,帶着“流浪”的作用域
閉包對於安裝計算,隱藏狀態,以及在函數對象和作用域中隨意的切換是很有用的
閉包也是函數,但是它們能攜帶一些額外的作用域
閉包實例
在電子商務平臺下,每件商品都有一個計數器說明該商品售出數量,這個計算器就可以通過閉包實現
>>> def counter(start_at = 0):
... count = [start_at]
... def incr():
... count[0] +=1
... return count[0]
... return incr
...
>>> a = counter()
>>> print a()
1
>>> b = counter(10)
>>> print b()
11
>>> print a()
2
>>> print a()
3
安裝Tkinter模塊
# sudo apt-get install python-tk
# vim button.py
#!/usr/bin/env python
from functools import partial
import Tkinter
def say_hi(word):
def greet():
print "hello %s!" % word
return greet
root = Tkinter.Tk()
MyButton = partial(Tkinter.Button,root,fg = 'white',bg='blue')
b1 = MyButton(text='Button1',command = say_hi('world'))
b2 = MyButton(text='Button2',command = say_hi('tedu'))
qb = MyButton(text='quit',command = root.quit)
b1.pack()
b2.pack()
qb.pack()
Tkinter.mainloop()
裝飾器
裝飾器是在函數調用至上的修飾
這些修飾僅是當聲明一個函數或者方法的時候,纔會應用的額外調用
使用裝飾器的情形有:
- 引入日誌
- 增加計時邏輯來檢測性能
- 給函數加入事物的能力
#!/usr/bin/env python
import time
def deco(func):
def timeit():
start = time.time()
res = func()
end = time.time()
print "Program cost %5.3f seconds" %(end - start)
return res
return timeit
@deco
def loop():
result = []
for i in range(1,6):
result.append(i)
time.sleep(1)
return result
if __name__ == '__main__':
print loop()
#!/usr/bin/env python
def color(func):
def color_font(*argv):
return "\033[31;1m%s\033[0m" % func(*argv)
return color_font
@color
def say_hi(word):
return "Hello %s." % word
@color
def greet(name):
return 'Welcome %s!' % name
if __name__ == '__main__':
print say_hi('tedu')
print greet('bob')
生成器
定義生成器
從句法上講,生成器是一個帶yield語句的函數
一個函數或者子程序只返回一次,但一個生成器能暫停執行並返回一箇中間的結果
yield語句返回一個值給調用者並暫停執行
當生成器的next()方法被調用的時候,它會準確的從離開地方繼續
>>> def sim_gen():
... yield 'hello'
... yield 100
... yield [10,20]
...
>>> a = sim_gen()
>>> a
<generator object sim_gen at 0xb70b761c>
>>> a.next()
'hello'
>>> a.next()
100
>>> a.next()
[10, 20]
>>> a.next()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
>>> a = sim_gen()
>>> for i in a:
... print i
...
hello
100
[10, 20]
#!/usr/bin/env python
def blocks(fobj):
block =[]
counter = 0
for line in fobj:
block.append(line)
counter +=1
if counter ==10:
yield block
counter = 0
block =[]
yield block
if __name__ == '__main__':
with open("/etc/passwd") as fobj:
for lines in blocks(fobj):
print lines
生成器特性
用戶可以通過send()將值回送給生成器,還可以在生成器中拋出異常,以及要求生成器退出
模塊
模塊和文件
什麼是模塊
模塊支持從邏輯上組織python代碼
當代碼量變得相當大的時候,最好把代碼分成一些有組織的代碼段
代碼片段相互間有一定的練習,可能是一個包含數據成員和方法的類,也可能是一組相關但彼此獨立的操作函數
這些代碼段是共享的,所以python允許“調入”一個模塊,允許使用其他模塊的屬性來利用之前的工作成果,實現代碼重用
模塊文件
說模塊十按照邏輯來組織python代碼的方法,文件是物理層上組織模塊的方法
一個文件被看作是一個獨立模塊,一個模塊也可以被看作是一個文件
模塊的文件名就是模塊的名字加上擴展名py
名稱空間
名稱空間就是一個從名稱到對象的關係映射集合
給定一個模塊名之後,只可能有一個模塊被導入到python解釋器中,所以不同模塊間不會出現名稱交叉現象
每個模塊都定義了它自己的唯一的名稱空間
echo 'x=10' > foo.py
echo 'x="hello"' > bar.py
>>> import foo
>>> import bar
>>> foo.x
10
>>> bar.x
'hello'
導入模塊
搜索路徑
模塊的導入需要一個叫做“路徑搜索”的過程
python在文件系統“預定義區域”中查找要調用的模塊
搜索路徑在sys.path中定義
>>> import sys
>>> print sys.path
['', '/usr/lib/python2.7', '/usr/lib/python2.7/plat-i386-linux-gnu', '/usr/lib/python2.7/lib-tk', '/usr/lib/python2.7/lib-old', '/usr/lib/python2.7/lib-dynload', '/usr/local/lib/python2.7/dist-packages', '/usr/lib/python2.7/dist-packages', '/usr/lib/python2.7/dist-packages/PILcompat', '/usr/lib/python2.7/dist-packages/ubuntu-sso-client']
模塊導入方法
使用import導入模塊
可以在一行導入多個模塊,但是可讀性會下降
可以只導入模塊的某些屬性
導入模塊時,可以爲模塊取別名
>>> import time,os,sys
>>> from random import choice
>>> import cPickle as P
避免模塊不存在導入失敗的小方法
>>> try:
... import cPickle as p
... except ImportError:
... import pickle as p
...
導入和加載
當導入模塊時,模塊的頂層代碼會被執行
一個模塊不管被導入(import)多少次,只會被加載(load)一次
$ echo -e "hi = 'hello'\nprint hi" > foo.py
>>> import foo #第一次導入,執行print語句
hello
>>> import foo #再次導入,print語句不再執行
從zip文件中導入
在2.3版中,python加入了從ZIP歸檔文件導入模塊的功能
如果搜索路徑中存在一個包含python模塊(.py、.pyc、或.pyo文件)的.zip文件,導入時會把ZIP文件當作目錄處理
#導入sys模塊,在搜索路徑中加入相應的zip文件
>>> import sys
>>> sys.path.append('/root/pymodule.zip')
>>> import foo #導入pymodule.zip壓縮文件中的foo模塊
包
目錄機構
包是一個有層次的目錄結構,爲平坦的名稱空間加入有層次的組織結構
允許程序員把有聯繫的模塊組合到一起
包目錄下必須有一個__init__.py文件
phone/
__init__.py
common_util.py
voicedate/
__init__.py
post.py
絕對導入
包的使用越來越廣泛,很多情況下導入子包會導致和真正的標準庫模塊發生衝突
因此,所有的導入現在都被認爲是絕對的,也就是說這些名字必須通過python路徑(sys.path或PYTHONPATH)來訪問
相對導入
絕對導入特性使得程序員失去了import的自由,爲此出現了相對導入
因爲import語句總是絕對導入的,所以相對導入只應用與from-import語句
內建模塊
hashlibs模塊
hashlib用來替換md5和sha模塊,並使他們的API一致,專門提供hash算法
包括md5,sha1,sha224,sha256,sha384,sha512,使用非常簡單、方便
>>> import hashlib
>>> m = hashlib.md5()
>>> m.update("hello world!")
>>> m.hexdigest()
'fc3ff98e8c6a0d3087d515c0473f8677'
>>> import hashlib
>>> m = hashlib.md5()
>>> with open('/root/qh/py/ex6.py') as fobj:
... while True:
... date = fobj.read(4096)
... if not date:
... break
... m.update(date)
...
>>> m.hexdigest()
'7daba44373fcb6c3aa8dfb25df1cf71a'
tarfile模塊
tarfile模塊允許創建、訪問tar文件
同時支持gzip、bzip2格式
>>> import tarfile
>>> tar = tarfile.open('/home/qihang/demo.tar.gz','w:gz') #創建tar文件,
>>> tar.add('demo') #向tar文件中添加要打包的文件
>>> tar.close()
備份程序
編寫一個備份程序
1、需要支持完全和增量備份
2、週一執行完全備份
3、其他時間執行增量備份
4、備份文件需要打包爲tar文件並使用gzip格式壓縮