Python快速入門教程【轉】

 

第一章 Python基礎知識

1.1 介紹
     1.1.1 特色
     Python是一種面向對象、解釋型計算機程序設計語言。語法簡潔清晰,強制用空白符做爲語句縮進。
     Python具備豐富和強大的庫,又被稱爲膠水語言。能把其餘語言(主要C/C++)寫的模塊很輕鬆的結合在一塊兒。
     1.1.2 應用領域
        Web網站:有不少優秀的開源Web框架,好比Django(最流行)、Tornado(輕量級、異步)、Flask(微型)、Web.py(簡單)等。
     數據採集:有好用的http庫,好比urllib二、requests等。還有高級的屏幕爬取及網頁採集框架scrapy。並對網頁解析也有不少庫,好比lxml、xpath、BeautifulSoup等。    
     大數據分析:經常使用模塊有Numpy、Pandas。並支持寫MapReduce、PySpark處理Spark RDD(彈性分佈式數據集)。
     運維自動化:編寫腳本、Web平臺,自動化平常工做。
     科學計算:在科學計算也應用愈來愈普遍,經常使用的模塊有Numpy、SciPy。
     等等...可見Python是一門通用語言!
1.1.3 爲何選擇Python?
     運維的目的呢,主要仍是學習Python用來實現運維自動化了。大多數人除了shell腳本外有其餘語言基礎的應該佔少數。
     咱們以Python做爲第一門語言是很好的選擇。爲何呢?
  1) 語法簡潔,易於學習。
  2) 普遍的標準庫,適合快速開發,不就追求極快處理速度。
  3) 跨平臺,基本全部的全部的操做系統都能運行。
  4) 運維領域Python最流行。
1.2  安裝Python
   操做系統採用CentOS6.5,默認安裝了Python2.6.6,那咱們升級到Python2.7最新版Python2.7.12
     1. 安裝Python2.7
     # wget https://www.python.org/ftp/python/2.7.12/Python-2.7.12.tgz
     # tar zxvf Python-2.7.12.tgz
     # cd Python-2.7.12
     # ./configure
     # make && make install
     # mv /usr/bin/python /usr/bin/python2.6.6
     # ln -s /usr/local/bin/python2.7 /usr/bin/python
     # python -V
     Python 2.7.12
     注意:軟連接指向Python2.7版本後,yum將不能正常工做,由於yum不兼容2.7的,全部須要指定下yum命令裏默認Python版本爲2.6.6版本
     # sed -i '1s/$/2.6.6/' /usr/bin/yum
     2. 安裝setuptools
     # yum install python-devel zlib-devel openssl-devel -y
     # wget https://pypi.python.org/packages/32/3c/e853a68b703f347f5ed86585c2dd2828a83252e1216c1201fa6f81270578/setuptools-26.1.1.tar.gz
     # tar zxvf setuptools-26.1.1.tar.gz 
     # cd setuptools-26.1.1
     # python setup.py install
     ......
         "Compression requires the (missing) zlib module"
     RuntimeError: Compression requires the (missing) zlib module
     解決方法,進入剛解壓的Python2.7目錄從新編譯安裝:
     # cd ../Python-2.7.12
     # make && make install
     # python setup.py install
     3. 安裝pip2.7
     # wget https://pypi.python.org/packages/e7/a8/7556133689add8d1a54c0b14aeff0acb03c64707ce100ecd53934da1aa13/pip-8.1.2.tar.gz
   # tar zxvf pip-8.1.2.tar.gz
   # cd pip-8.1.2
   # python setup.py install
 
1.3  解釋器
     1.3.1 Python解釋器幾種實現版本
         1) CPython     
              當咱們裝完Python後,其默認解釋就是CPython,也是官方默認解釋器。CPython是C語言寫的,當執行代碼時會將代碼轉化成字節碼(ByteCode)。
         2) IPython
              基於CPython之上的一個交互式解釋器,至關於默認解釋器的一個加強版,最顯著的功能就是自動補全,挺好用的。
         3) PyPy
              PyPy自己是由Python編寫的,使用了JIT編譯器(即時編譯器)技術,當執行代碼時JIT編譯器將代碼翻譯成機器碼。性能相比CPython要好。JAVA也採用了JIT編譯器。
         4) Jython
              Jython是由JAVA編寫的一個解釋器,能夠把JAVA模塊加載到Python的模塊中使用,也能夠把Python代碼打包成JAR包,意味着容許用Python寫JAVA程序了。當執行代碼時會將代碼轉化成JAVA字節碼,而後使用JRE執行。
         5) IronPython
              在.NET平臺上工做的Python語言。
     1.3.2 Python代碼執行過程
         大體流程:源代碼編譯成字節碼(.pyc文件)--> Python虛擬機 --> 執行編譯好的字節碼 --> Python虛擬機將字節碼翻譯成對應的機器指令(機器碼)
         運行Python程序時,先編譯成字節碼並保存到內存中,當程序運行結束後,Python解釋器將內存中字節碼對象寫到.pyc文件中。
         第二次再運行此程序時,先回從硬盤中尋找.pyc文件,若是找到,則直接載入,不然就重複上面的過程。
         這樣好處是,不重複編譯,提供執行效率。
         1) 字節碼
              字節碼是一種包含執行程序、由一序列op代碼/數據對組成的二進制文件。字節碼是一種中間碼,比機器碼更抽象。
         2) 機器碼
              機器碼是一種指令集,讓CPU可直接解讀的數據。也稱爲原生碼。
1.4 代碼風格
     1.4.1 代碼風格有毛用?
          我的以爲有如下幾個做用:
          1) 團隊協做     
              在企業中,一個團隊開發一個項目很正常不過了,剛入職是否是會先讓你熟悉本公司的編碼規範文檔呢,做爲純開發來講,我相信大多數公司都會這麼作,其中目的是讓團隊中的每一個成員,寫代碼時可以統一,避免項目中出現幾個編碼風格版本,不利用後期維護和交接。
          2) 有利於解決問題
              草泥馬,又出問題了,代碼運行不起來了,怎麼辦?百度、谷歌無解...是時候求助大神了,來看看個人代碼吧!大神一看,琢磨了一會,你想多了,不是再想你的問題,而是在梳理你的代碼實現的功能和邏輯關係。結果發現,多了括號。擦,我怎麼就沒看到呢!~
          3) 未雨綢繆
              功能終於實現了,發佈到線上運行也挺正常,過了半年後,忽然跑不起來了,趕忙排查問題,代碼看着看着本身就懵逼了,這仍是本身寫的代碼嘛,長的這麼不像我,是親生的嘛!
          小結:只要人人都獻出一點愛,世界將會變成美好的人間。
     1.4.2 編寫代碼怎麼能更規範化?
          1) 縮進
              Python以空白符做爲語句縮進,意味着語句沒有結尾符,給每每由於少寫個fi的人帶來了福利,在Python中最好以4個空格做爲縮進符。
          2) 代碼註釋
              聽說優質的代碼,註釋說明要比代碼量多,詳細的代碼說明無論對本身後期維護仍是開源,都是有必要的。就像一個流行的軟件,若是沒有豐富的使用文檔,你認爲會有多少耐心的人去花大把的時間研究它呢!
          3) 空格使用
              在操做符兩邊,以及逗號後面,加1個空格。可是在括號左右不加空格。
              在函數、類、以及某些功能代碼塊,空出一行,來分隔它們。
          4) 命名
              模塊:本身寫的模塊,文件名所有小寫,長名字單詞如下劃線分隔。
              類:大/小駝峯命名。我通常採用大駝峯命名,也就是每一個單詞首字母大寫。類中私有屬性、私有方法,以雙下劃線做爲前綴。
              函數:首單詞小寫,其他首字母大寫。
              變量:都小寫,單詞如下劃線分隔。
               提醒:全部的命名必須能簡要說明此代碼意義。
          5) 代碼換行
              按照語法規則去換行,好比一個很長的表達式,能夠在其中某個小表達式兩邊進行換行,而不是將小表達式拆分,這樣更容易閱讀。
1.5 交互式解釋器
    直接執行Python命令就啓動默認的CPython解釋器:
# python
Python 2.7.12 (default, Sep  3 2016, 21:51:00)
[GCC 4.4.7 20120313 (Red Hat 4.4.7-17)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> print "Hello World"
Hello World
 
配置自動補全:
# pip2.7 install readline
# pip2.7 install rlcompleter2
>>> import readline, rlcompleter
>>> readline.parse_and_bind("tab: complete")
1.6 運算操做符  
運算符
描述
示例
+
加法
1 + 1 = 2
-
減法
3 - 1 = 2
*
乘法
2 * 1 = 2
/
除法
2 / 1 = 2
%
取餘/模
2 % 1 = 2
**
指數/冪
2 ** 1 = 2
 
1.7 賦值操做符
操做符
描述
示例
=
變量賦值
a = b + c
+=
加法
a += b 等同於 a = a + b
-=
減法
a -= b 等同於 a = a - b
*=
乘法
a *= b 等同於 a = a * b
/=
除法
a /= b 等同於 a = a / b
%=
a %= b 等同於 a = a % b
**=
指數/冪
a **= b 等同於 a = a ** b
賦值操做符,操做符左邊運算右邊,而後將結果賦值給操做符左邊。
 
博客地址:http://lizhenliang.blog.51cto.com and https://yq.aliyun.com/u/lizhenliang
QQ羣:323779636(Shell/Python運維開發羣)
 
1.8 變量
    1.8.1 變量賦值
>>> xxoo = 2
>>> print xxoo
>>> 2
說明:等號左邊是變量名,等號右邊是值
# 多重賦值
>>> xx, oo = 1, 2
>>> print xx
1
>>> print oo
2
>>> xx = oo = 2
>>> print xx
2
>>> print oo
2
     1. 8.2 變量引用
     上面打印時就是在引用變量了,可見Python引用變量不用加$什麼特殊字符,不像Shell、PHP那樣,還要加$。
     的確,直接用變量名便是引用,下面說一種經常使用的字符串格式輸出時引用變量的方法。
>>> xxoo = 2
>>> print "xxoo: %d" % xxoo
xxoo: 2
>>> xxoo = "xo"
>>> print "xxoo: %s" % xxoo
xxoo: xo
>>> x = "abc"
>>> o = 123
>>> print "str: %s, int: %d" %(x, o)  
str: abc, int: 123
     說明:雙引號裏面%操做符算是佔位符吧,d表明數字,s表明字符串。雙引號外面%加上後面的變量名對應裏面的第一個%。
         下面同時引用了兩個變量,外面%()裏變量名位置對應雙引號裏面的%位置。
    1.8.3 局部變量
>>> xxoo = 2 
>>> print xxoo
2
    1.8.4 全局變量
>>> global xxoo   # 聲明爲全局變量
>>> print xxoo
2
    說明:從上面並不能看出什麼區別,後續在函數章節中會講解局部變量和全局變量的區別和使用。
 
1.9 轉義字符(列出一些經常使用的)
符號
描述
\
字符串太長,換一行接着輸入
\'   \"
單引號和雙引號
\r
光標
\t
橫向製表符(tab鍵)
\v
縱向製表符
\n
換行符,打印到下一行
示例:
>>> print "Hello \
... World"       
Hello World
>>> print "Hello \"World!"
Hello "World!
>>> print "Hello \rWorld!"
World!
>>> print "Hello\tWorld!"
Hello   World!
>>> print "Hello \vWorld!"
Hello
      World!
>>> print "Hello \nWorld!"
Hello
World!
 
若是不想讓轉義字符生效,能夠用r指定顯示原始字符串:
>>> print r"Hello \nWorld!"
Hello \nWorld!
>>> print "Hello \nWorld!"
Hello
World!
 
1.10 獲取用戶輸入
    1.10.1 raw_input()  
>>> name = raw_input("My name is: ")
My name is: xiaoming
>>> print name
xiaoming
    1.10.2 input()
>>> name = input("My name is: ")
My name is: xiaoming
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<string>", line 1, in <module>
NameError: name 'xiaoming' is not defined
 
>>> name = input("My name is: ")
My name is: "xiaoming"
>>> print name
xiaoming
>>> name = input("My name is: ")
My name is: 1 + 2
>>>
>>> print name
3
    1.10.3 raw_input()與input()函數區別
  能夠看到兩個函數用一樣的方式輸入,結果input()報錯!     
  緣由是由於raw_input()把任何輸入的都轉成字符串存儲。
  而input()接受輸入的是一個表達式,不然就報錯。
 
1.11 運行第一個程序
# vi test.py
#!/usr/bin/env python  # 說明用什麼可執行程序運行它,env會自動尋找python解釋器的絕對路徑
print "Hello World!"
 
# python test.py
Hello World!
    easy!打印Hello world已經沒什麼難度了,那改進下剛學接受用戶輸入。
# vi test.py
#!/usr/bin/env python
name = raw_input("My name is: ")
print name
 
# python test.py
My name is: xiaoming
xiaoming
 
1.12 註釋
   單行註釋:井號("#")開頭
   多行註釋:三單引號或三雙引號
#!/usr/bin/env python
# -*- coding: utf-8 -*-   # 設置解釋器默認編碼,下一章會講到
 
# 單行註釋
 
'''
多行註釋
多行註釋
'''
 
"""
多行註釋
多行註釋

第二章 字符串處理與編碼再也不發愁

2.1 字符串
   2.1.1 字符串轉換
>>> a = 123
>>> b = 1.23
>>> type(a)
<type 'int'>
>>> type(b)
<type 'float'>
>>> type(str(a))
<type 'str'>
>>> type(str(b))
<type 'str'>
說明:先定義個整數和浮點數,再查看類型,用str()函數將對象轉成字符串。
這裏的用到了type()函數,用於查看對象類型。這個type()在之後學習中很用的,剛開始學習時候,每每由於對象類型不對,致使程序運行報錯,這時能夠用它來排查問題。 
   2.1.2 字符串鏈接
# 加號字符將同類型字符鏈接到一塊兒
>>> hw = "Hello" + "World!"
>>> print hw
HelloWorld!
 
# 兩個相鄰的字符串自動鏈接一塊兒
>>> hw = "Hello""World!"
>>> print hw
HelloWorld!
 
# 若是字符串內包括單引號或雙引號,要用\轉義,不然報錯,上一章也講過。
>>> hw = "Hello \"World!\""
>>> print hw
Hello "World!"
 
# 不一樣字符串類型拼接
>>> a = "abc"
>>> b = 1
>>> print a + b
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: cannot concatenate 'str' and 'int' objects
說明:不一樣字符串類型不容許鏈接,想要鏈接能夠下面這麼作。
方法1:
>>> c = "%s%d" %(a,b)
>>> print c
abc1
方法2:
>>> c = a + str(b)
>>> print c
abc1
   2.1.3 格式化輸出
操做符號
說明
%s
字符串(str())
%r
字符串(repr())
%d
整數
%f
浮點數,可指定小數點後的精度
 
       1) 字符串格式輸出三種方法
>>> xxoo = "string"
>>> print "%s" %xxoo
string
>>> print "%r" %xxoo
'string'
>>> print `xxoo`   
'string'
        說明:%s採用str()函數顯示,%r採用repr()函數顯示。repr()和反撇號把字符串轉爲Python表達式。
       2) 保留小數點數
>>> '%.1f' %(float(100)/1024)
'0.1'
     2.1.4 字符串處理
      173d804d5e8a1287922f1436a9becbb3fead5453
     上圖是字符串處理的方法,紅色框框中大概有一半常常用的,咱們就拿一部分經常使用的來舉例說明。
#!/usr/bin/env python
# -*- coding: utf-8 -*-
xxoo = "Hello world!"
 
print "字符串長度: %s" % len(xxoo)
print "首字母大寫: %s" % xxoo.capitalize()
print "字符l出現次數: %s" % xxoo.count('l')
print "感嘆號是否結尾: %s" % xxoo.endswith('!')
print "w字符是不是開頭: %s" % xxoo.startswith('w')
print "w字符索引位置: %s" % xxoo.find('w') # xxoo.index('W')
print "格式化字符串: Hello{0} world!".format(',')
print "是否都是小寫: %s" % xxoo.islower()
print "是否都是大寫: %s" % xxoo.isupper()
print "全部字母轉爲小寫: %s" % xxoo.lower()
print "全部字母轉爲大寫: %s" % xxoo.upper()
print "感嘆號替換爲句號: %s" % xxoo.replace('!','.')
print "以空格分隔切分紅列表: %s" % xxoo.split(' ')
print "轉換爲一個列表: %s" % xxoo.splitlines()
print "去除兩邊空格: %s" % xxoo.strip()
print "大小寫互換: %s" % xxoo.swapcase()
print "只要Hello字符串: %s" % xxoo[0:5]
print "去掉倒數第一個字符: %s" % xxoo[0:-1]
 
# python test.py
字符串長度: 12
首字母大寫: Hello world!
字符l出現次數: 3
感嘆號是否結尾: True
w字符是不是開頭: False
w字符索引位置: 6
格式化字符串: Hello, world!
是否都是小寫: False
是否都是大寫: False
全部字母轉爲小寫: hello world!
全部字母轉爲大寫: HELLO WORLD!
感嘆號替換爲句號: Hello world.
以空格分隔切分紅列表: ['Hello', 'world!']
轉換爲一個列表: ['Hello world!']
去除兩邊空格: Hello world!
大小寫互換: hELLO WORLD!
只要Hello字符串: Hello
去掉倒數第一個字符: Hello world
 
博客地址:http://lizhenliang.blog.51cto.com and https://yq.aliyun.com/u/lizhenliang
QQ羣:323779636(Shell/Python運維開發羣)
 
2.2 編碼
   2.2.1 常見字符編碼類型
     ASCII:美國信息交換標準碼,是目前計算機中最普遍使用的字符集編碼。每一個ASCII碼以1個字節存儲,例如數字字符0的ASCII碼是0110000,十進制表示爲48。
     Unicode:爲解決世界上上百種語言帶來混合、衝突,各國有各國的標準,顯示很容易出現亂碼。Unicode就出現了,它把全部語言的字符都統一到一套Unicode編碼中,並定義每一個語言字符的標準,因此Unicode又稱統一碼,萬國碼。大部分編程語言都支持Unicode,Python內部編碼也支持Unicode。
     GB2312:中國國家標準總局發佈處理漢字的標準編碼。
     GBK:GB2312的擴展,向下兼容GB2312。 
     UTF-8:針對Unicode的可變長度字符編碼,又稱萬國碼。支持中文簡體繁體及其它語言(如英文,日文,韓文)。
    2.2. 3 decode()
     decode()函數做用是將其餘編碼(好比ACSII、Byte String)的字符串解碼成Unicode。
    2.2. 4 encode()
     encode()函數做用是將Unicode編碼成終端軟件能是識別的編碼,就能正常顯示了,好比UTF-八、GBK。
    2.2. 5 Python編碼處理
#!/usr/bin/env python
c = "中文"
print c
# python test.py
  File "test.py", line 2
SyntaxError: Non-ASCII character '\xe4' in file test.py on line 3, but no encoding declared; see http://www.python.org/peps/pep-0263.html for details
說明:在程序裏面直接打印中文,會報語法錯誤,這是由於Python默認編碼是ASCII,沒法處理其餘編碼。
若是想打印中文,須要聲明編碼爲utf-8,上面也有寫過:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
c = "中文"
print c
print type(c)
# python test.py
中文
<type 'str'>
能夠正常輸出中文了,類型是字符串,這個字符串是通過Python unicode編碼後字節組成的。
雖然能夠正常輸入中文,並不意味的就萬事大吉了,若是終端編碼不是utf-8或其餘軟件也不肯定編碼還會出現亂碼狀況。因此仍是要明白Python處理編碼邏輯關係,才能更好的應對編碼問題。
   切換到交互式解釋器:
>>> c = "中文"
>>> c.encode('utf-8')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeDecodeError: 'ascii' codec can't decode byte 0xe4 in position 0: ordinal not in range(128)
    若是直接轉成utf-8是不容許的,報錯Unicode解碼錯誤,大概意思是說ascii碼不能解碼字節字符串。
    上面講到encode()函數做用是將Unicode碼解碼,而如今的c變量並不是是Unicode碼,而是字節字符串,算是Unicode的一種吧?。
    故此,不能使用encode(),而是先使用decode()先解碼陳Unicode再用encode()編碼成utf-8。
>>> c.decode('utf-8')
u'\u4e2d\u6587'       # 4e2d對應unicode值是"中",6587對應unicdoe值是"文"
>>> type(c.decode('utf-8'))
<type 'unicode'>
>>> print c.decode('utf-8')    ?
中文
>>> print c.decode('utf-8').encode('utf-8')
中文
    若是是Unicode字符串可直接經過encode()函數轉碼其餘編碼。
>>> c = u'中文'
>>> c.encode('utf-8')
'\xe4\xb8\xad\xe6\x96\x87'
>>> print c.encode('utf-8')
中文
    看下字節字符串和unicode字符串區別:
>>> c = '中文'
>>> u = u'中文'
>>> c
'\xe4\xb8\xad\xe6\x96\x87'
>>> u
u'\u4e2d\u6587'
>>> len(c)
6
>>> len(u)
2
    字節字符串長度要比unicode長的多,而unicode長度就是字符長度。
    總結下:Python處理編碼流程大體是這樣的,ascii --> decode() --> unicode --> encode() --> 終端是能識別的編碼,unicode算是一箇中間碼,有着承上啓下的做用。

第三章 Python豐富的數據類型

什麼是數據類型?
前兩章裏面包含的字符串、布爾類型、整數、浮點數都是數據類型。數據類型在一個編程語言中必不可少,也是使用最多的。
並且數據類型的數據都是存放在內存中的,咱們通常操做都是在對內存裏對象操做。
什麼是數組?
   數組也是一種數據類型,爲了方便處理數據,把一些同類數據放到一塊兒就是數組,是一組數據的集合,數組內的數據稱爲元素,每一個元素都有一個下標(索引),從0開始。
在Python中,內建數據結構有列表(list)、元組(tuple)、字典(dict)、集合(set)。
3.1 列表[List]
   3.1.1 定義列表
     >>> lst = ['a','b','c',1,2,3]
   用中括號括起來,元素以逗號分隔,字符串用單引號引發來,整數不用。
   58a0076f47a5f70af85ad3f76e518bcd6213bd0f
   3.1.2 基本操做
# 追加一個元素
>>> lst.append(4)
>>> lst
['a', 'b', 'c', 1, 2, 3, 4]
# 統計列表中a字符出現的次數
>>> lst.count('a')
1
# 將一個列表做爲元素添加到lst列表中
>>> a = [5,6]
>>> lst.extend(a)
>>> lst
['a', 'b', 'c', 1, 2, 3, 4, 5, 6]
# 查找元素3的索引位置
>>> lst.index(1)
3
# 在第3個索引位置插入一個元素
>>> lst.insert(3, 0)
>>> lst
['a', 'b', 'c', 0, 1, 2, 3, 4, 5, 6]
# 刪除最後一個元素和第3個下標元素
>>> lst.pop()  
6
>>> lst.pop(3)
0
>>> lst
['a', 'b', 'c', 1, 2, 3, 4, 5]
# 刪除元素是5,若是沒有會返回錯誤
>>> lst.remove("5")
>>> lst
['a', 'b', 'c', 1, 2, 3, 4]
# 倒序排列元素
>>> lst.reverse()
>>> lst
[4, 3, 2, 1, 'c', 'b', 'a']
# 正向排序元素
>>> lst.sort()
>>> lst
[1, 2, 3, 4, 'a', 'b', 'c']
# 列表鏈接
>>> a = [1,2,3]
>>> b = ['a','b','c']
>>> a + b
[1, 2, 3, 'a', 'b', 'c']
   3.1.3 學習新函數對列表排序
# reversed()函數倒序排列
使用此函數會建立一個迭代器,遍歷打印才能輸出:
>>> lst = ['a', 'b', 'c', 1, 2, 3, 4, 5]
>>> type(reversed(lst))
<type 'listreverseiterator'>
>>> lst2 = []
>>> for i in reversed(lst):
...   lst2.append(i)
...
>>> lst2
[5, 4, 3, 2, 1, 'c', 'b', 'a']
# sorted()函數正向排列
>>> lst2 = []
>>> for i in sorted(lst):
...   lst2.append(i)
...
>>> lst2
[1, 2, 3, 4, 5, 'a', 'b', 'c']
 
這裏在講解一個序列生成器range()函數,生成的是一個列表:
>>> type(range(5))
<type 'list'>
>>> for i in range(1,5):
...   print i
...
1
2
3
4
固然也能夠用上面的排序函數來排序這個生成的序列了:
>>> for i in reversed(range(1,10,3)):
...   print i
...
7
4
1
range()函數用法:range(start,end,step)
   說明:是否是和列表內置方法結果同樣!區別是內置函數不改動原有序列。
   3.1.4 切片
>>> lst
[1, 2, 3, 4, 'a', 'b', 'c']
# 返回第一個元素
>>> lst[0]
1
# 返回倒數第一個元素
>>> lst[-1]
'c'
# 取出倒數第一個元素
>>> lst[0:-1]
[1, 2, 3, 4, 'a', 'b']
# 返回第一個至第四個元素
>>> lst[0:4]
[1, 2, 3, 4]
   3.1.5 清空列表
方法1:
>>> lst = [1, 2, 3, 4, 'a', 'b', 'c']
>>> lst = []
>>> lst
[]
方法2:
>>> lst = [1, 2, 3, 4, 'a', 'b', 'c']
>>> del lst[:]
>>> lst
[]
# 刪除列表
>>> lst = [1, 2, 3, 4, 'a', 'b', 'c']
>>> del lst
>>> lst
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'lst' is not defined
   3.1.6 del語句
   del語句也能夠刪除一個下標範圍的元素
>>> lst = [1, 2, 3, 4, 'a', 'b', 'c']
>>> del lst[0:4]
>>> lst
['a', 'b', 'c']
   3.1.7 列表推導式
   利用其它列表推導出新的列表。
# 經過迭代對象方法
方法1:
>>> lst = []
>>> for i in range(5):
...   lst.append(i)
...
>>> lst
[0, 1, 2, 3, 4]
方法2:
>>> lst = []
>>> lst = [i for i in range(5)]
>>> lst
[0, 1, 2, 3, 4]
說明:方法1和方法2,實現方式是同樣的,只是方法2用簡潔的寫法。for循環在下一章會講。
 
# 經過已有的列表生成新列表
>>> lst
[0, 1, 2, 3, 4]
>>> lst2 = [i for i in lst if i > 2]
>>> lst2
[3, 4]
   3.1.8 遍歷列表
   若是既要遍歷索引又要遍歷元素,能夠這樣寫。
方法1:
>>> lst = ['a','b','c',1,2,3]
>>> for i in range(len(lst)):
...   print i,lst[i]         
...
0 a
1 b
2 c
3 1
4 2
5 3
方法2:
>>> for index, value in enumerate(lst):
...   print index,value
...
0 a
1 b
2 c
3 1
4 2
5 3
   又學了一個新函數enumrate(),可遍歷列表、字符串的下標和元素。
3.2 元組(Tuple)
   元組與列表類型,不一樣之處在於元素的元素不可修改。
   2.1 定義元組
     t = ('a','b','c',1,2,3)
    用小括號括起來,元素以逗號分隔,字符串用單引號引發來,整數不用。       
   2.2 基本操做
   count()和index()方法和切片使用方法與列表使用同樣,這裏再也不講解。
3.3 集合(set)
   集合是一個無序不重複元素的序列,主要功能用於刪除重複元素和關係測試。
   集合對象還支持聯合(union),交集(intersection),差集(difference)和對稱差集(sysmmetric difference)數學運算。
   須要注意的是,集合對象不支持索引,所以不能夠被切片。
   3.3.1 定義集合
      >>> s = set()
>>> s
set([])
    使用set()函數建立集合。   
3.3.2 基本操做
# 添加元素
>>> s.add('a')
>>> s
set(['a'])
>>> s.add('b')
>>> s
set(['a', 'b'])
>>> s.add('c')
>>> s
set(['a', 'c', 'b'])
>>> s.add('c')
>>> s
set(['a', 'c', 'b'])
說明:能夠看到,添加的元素是無序的,而且不重複的。
 
# update方法事把傳入的元素拆分爲個體傳入到集合中。與直接set('1234')效果同樣。
>>> s.update('1234')
>>> s
set(['a', 'c', 'b', '1', '3', '2', '4'])
# 刪除元素
>>> s.remove('4')   
>>> s
set(['a', 'c', 'b', '1', '3', '2'])
# 刪除元素,沒有也不會報錯,而remove會報錯
>>> s.discard('4')   
>>> s
set(['a', 'c', 'b', '1', '3', '2'])
# 刪除第一個元素
>>> s.pop()
'a'
>>> s
set(['c', 'b', '1', '3', '2'])
# 清空元素
>>> s.clear()
>>> s
set([])
 
# 列表轉集合,同時去重
>>> lst = ['a','b','c',1,2,3,1,2,3]
>>> s = set(lst)
>>> s
set(['a', 1, 'c', 'b', 2, 3])
3.3.3 關係測試
符號
描述
-
差集
&
交集
|
合集、並集
!=
不等於
==
等於
in
是成員爲真
not in
不是成員爲真
 示例:
# 返回差集
>>> a - b
set(['1', '3', '2'])
>>> b - a
set(['9', '8', '7'])
# 返回交集
>>> a & b
set(['5', '4', '6'])
# 返回合集
>>> a | b
set(['1', '3', '2', '5', '4', '7', '6', '9', '8'])
# 不等於
>>> a != b
True
# 等於
>>> a == b
False
# 存在爲真
>>> '1' in a
True
# 不存在爲真
>>> '7' not in a
True
 
博客地址:http://lizhenliang.blog.51cto.com and https://yq.aliyun.com/u/lizhenliang
QQ羣:323779636(Shell/Python運維開發羣)
 
3.4 字典{Dict}
   序列是以連續的整數位索引,與字典不一樣的是,字典以關鍵字爲索引,關鍵字能夠是任意不可變對象(不可修改),一般是字符串或數值。
   字典是一個無序鍵:值(Key:Value)集合,在一字典中鍵必須是互不相同的,
   3.4.1 定義字典
     >>> d = {'a':1, 'b':2, 'c':3}
   用大括號括起來,一個鍵對應一個值,冒號分隔,多個鍵值逗號分隔。
   3.4.2 基本操做
# 返回全部鍵值
>>> d.items()
[('a', 1), ('c', 3), ('b', 2)]
# 返回全部鍵
>>> d.keys()
['a', 'c', 'b']
# 查看全部值
>>> d.values()
[1, 3, 2]
# 添加鍵值
>>> d['e'] = 4
>>> d
{'a': 1, 'c': 3, 'b': 2, 'e': 4}
# 獲取單個鍵的值,若是這個鍵不存在就會拋出KeyError錯誤
>>> d['a']
>>> 1
# 獲取單個鍵的值,若是有這個鍵就返回對應的值,不然返回自定義的值no
>>> d.get('a','no')
1
>>> d.get('f','no')
no
# 刪除第一個鍵值
>>> d.popitem()
('a', 1)
>>> d
{'c': 3, 'b': 2, 'e': 4}
# 刪除指定鍵
>>> d.pop('b')
2
>>> d
{'c': 3, 'e': 4}
# 添加其餘字典鍵值到本字典
>>> d
{'c': 3, 'e': 4}
>>> d2 = {'a':1}
>>> d.update(d2) 
>>> d
{'a': 1, 'c': 3, 'e': 4}
# 拷貝爲一個新字典
>>> d
{'a': 1, 'c': 3, 'e': 4}
>>> dd = d.copy()
>>> dd
{'a': 1, 'c': 3, 'e': 4}
>>> d
{'a': 1, 'c': 3, 'e': 4}
# 判斷鍵是否在字典
>>> d.has_key('a')
True
>>> d.has_key('b')
False
    3.4.3 可迭代對象
   字典提供了幾個獲取鍵值的迭代器,方便咱們在寫程序時處理,就是下面以iter開頭的方法。
d.iteritems()  # 獲取全部鍵值,很經常使用
d.iterkeys()   # 獲取全部鍵
d.itervalues() # 獲取全部值
 
# 遍歷iteritems()迭代器
>>> for i in d.iteritems():
...   print i
...
('a', 1)
('c', 3)
('b', 2)
說明:以元組的形式打印出了鍵值
若是咱們只想獲得鍵或者值呢,就能夠經過元組下標來分別獲取鍵值:
>>> for i in d.iteritems():   
...   print "%s:%s" %(i[0],i[1])
...
a:1
c:3
b:2
有比上面更好的方法實現:
>>> for k, v in d.iteritems():
...   print "%s: %s" %(k, v)   
...
a: 1
c: 3
b: 2
這樣就能夠很方面處理鍵值了!
 
# 遍歷其餘兩個迭代器也是一樣的方法
>>> for i in d.iterkeys():
...   print i
...
a
c
b
>>> for i in d.itervalues():
...   print i
...
1
3
2
   說明:上面用到了for循環來遍歷迭代器,for循環的用法在下一章會詳細講解。
   3.4.4 一個鍵多個值
    一個鍵對應一個值,有些狀況沒法知足需求,字典容許一個鍵多個值,也就是嵌入其餘數組,包括字典自己。
      # 嵌入列表
>>> d = {'a':[1,2,3], 'b':2, 'c':3}
>>> d['a']    
[1, 2, 3]  
>>> d['a'][0]  # 獲取值
1
>>> d['a'].append(4)  # 追加元素
>>> d
{'a': [1, 2, 3, 4], 'c': 3, 'b': 2}
# 嵌入元組
>>> d = {'a':(1,2,3), 'b':2, 'c':3}   
>>> d['a'][1]
2
# 嵌入字典
>>> d = {'a':{'d':4,'e':5}, 'b':2, 'c':3}   
>>> d['a']
{'e': 5, 'd': 4}
>>> d['a']['d']    # 獲取值
4
>>> d['a']['e'] = 6  # 修改值
>>> d
{'a': {'e': 6, 'd': 4}, 'c': 3, 'b': 2}
 
3.5 額外的數據類型
   colloctions()函數在內置數據類型基礎上,又增長了幾個額外的功能,替代內建的字典、列表、集合、元組及其餘數據類型。
   3.5.1 namedtuple
   namedtuple函數功能是使用名字來訪問元組元素。
      語法:namedtuple("名稱", [名字列表])
 
>>> from collections import namedtuple
>>> nt = namedtuple('point', ['a', 'b', 'c'])
>>> p = nt(1,2,3)
>>> p.a
1
>>> p.b
2
>>> p.c
3
   namedtuple函數規定了tuple元素的個數,並定義的名字個數與其對應。
   3.5.2 deque
   當list數據量大時,插入和刪除元素會很慢,deque的做用就是爲了快速實現插入和刪除元素的雙向列表。
>>> from collections import deque
>>> q = deque(['a', 'b', 'c'])
>>> q.append('d')
>>> q
deque(['a', 'b', 'c', 'd'])
>>> q.appendleft(0)
>>> q
deque([0, 'a', 'b', 'c', 'd'])
>>> q.pop()
'd'
>>> q.popleft()
0
   實現了插入和刪除頭部和尾部元素。比較適合作隊列。
   3.5.3 Counter
   顧名思義,計數器,用來計數。
   例如,統計字符出現的個數:
>>> from collections import Counter
>>> c = Counter()
>>> for i in "Hello world!":
...   c[i] += 1             
...
>>> c
Counter({'l': 3, 'o': 2, '!': 1, ' ': 1, 'e': 1, 'd': 1, 'H': 1, 'r': 1, 'w': 1})
   結果是以字典的形式存儲,實際Counter是dict的一個子類。
   3.5.4 OrderedDict
   內置dict是無序的,OrderedDict函數功能就是生成有序的字典。
   例如,根據先後插入順序排列:
>>> d = {'a':1, 'b':2, 'c':3}
>>> d   # 默認dict是無序的
{'a': 1, 'c': 3, 'b': 2}
 
>>> from collections import OrderedDict
>>> od = OrderedDict()
>>> od['a'] = 1
>>> od['b'] = 2
>>> od['c'] = 3
>>> od
OrderedDict([('a', 1), ('b', 2), ('c', 3)])
 
# 轉爲字典
>>> import json 
>>> json.dumps(od)
'{"a": 1, "b": 2, "c": 3}'
    OrderedDict輸出的結果是列表,元組爲元素,若是想返回字典格式,能夠經過json模塊進行轉化。
 
3.6 數據類型轉換
   3.6.1 常見數據類型轉換
# 轉整數
>>> i = '1'
>>> type(i)
<type 'str'>
>>> type(int(i))
<type 'int'>
# 轉浮點數
>>> f = 1
>>> type(f)
<type 'int'>
>>> type(float(f))
<type 'float'>
# 轉字符串
>>> i = 1
>>> type(i)
<type 'int'>
>>> type(int(1))
<type 'int'>
# 字符串轉列表
方式1:
>>> s = 'abc'
>>> lst = list(s)
>>> lst
['a', 'b', 'c']
方式2:
>>> s = 'abc 123'
>>> s.split()   
['abc', '123']
# 列表轉字符串
>>> s = ""
>>> s = ''.join(lst)
>>> s
'abc'
# 元組轉列表
>>> lst
['a', 'b', 'c']
>>> t = tuple(lst)
>>> t
('a', 'b', 'c')
# 列表轉元組
>>> lst = list(t)
>>> lst
['a', 'b', 'c']
# 字典格式字符串轉字典
方法1:
>>> s = '{"a": 1, "b": 2, "c": 3}'
>>> type(s)
<type 'str'>
>>> d = eval(s)
>>> d
{'a': 1, 'c': 3, 'b': 2}
>>> type(d)
<type 'dict'>
方法2:
>>> import json
>>> s = '{"a": 1, "b": 2, "c": 3}'
>>> json.loads(s)
{u'a': 1, u'c': 3, u'b': 2}
>>> d = json.loads(s)
>>> d
{u'a': 1, u'c': 3, u'b': 2}
>>> type(d)
<type 'dict'>
   3.6.2 學習兩個新內建函數
       1) join()
         join()函數是字符串操做函數,用於字符串鏈接。
# 字符串時,每一個字符做爲單個體
>>> s = "ttt"
>>> ".".join(s)
't.t.t'
# 以逗號鏈接元組元素,生成字符串,與上面的列表用法同樣。
>>> t = ('a', 'b', 'c')
>>> s = ",".join(t)
>>> s
'a,b,c'
           # 字典
>>> d = {'a':1, 'b':2, 'c':3}
>>> ",".join(d)
'a,c,b'
       2) eval()
         eval()函數將字符串當成Python表達式來處理。
>>> s = "abc"
>>> eval('s')
'abc'
>>> a = 1
>>> eval('a + 1')
2
>>> eval('1 + 1')
2

第四章 Python運算符和流程控制

在第一章的時候講解了運算操做符和賦值操做符,這章來學習下其餘經常使用操做符。
4.1 基本運算符
4.1.1 比較操做符
操做符
描述
示例
==
相等
>>> 1 == 1  True
!=
不相等
>>> 1 != 1 False
>
大於
>>> 2 > 1   True
<
小於
>>> 2 < 1  False
>=
大於等於
>>> 1 >= 1  True
<=
小於等於
>>> 1 <= 1  True
4.1.2 邏輯運算符
邏輯運算符經常使用於表達式判斷。
操做符
描述
and
or
not
示例:
>>> a = "a"
>>> b = "b"
>>> a and b
'b'
>>> a or b
'a'
>>> a = ""
>>> b = "b"
>>> a and b
''
>>> a or b
'b'
and操做符判斷表達式,若是a和b都爲真,返回b的值,不然返回a的值。
or操做符也是判斷表達式,若是a和b都爲真,返回a的值,不然返回b的值。
相似於shell裏的&&和||:[ 'a' == 'b' ] && echo no || echo yes
>>> a = ""
>>> if not a:   
...   print "yes"
... else:       
...   print "no"
...
yes
>>> a = "a"
>>> if not a:   
...   print "yes"
... else:
...   print "no"
...
no
not操做符用於布爾值(true和false)判斷不爲真,與if語句連用。上面是不爲真用not,那爲真時怎麼弄呢?
>>> a = "a"
>>> if a:
...   print "yes"
... else:
...   print "no"
...
yes
>>> a = ""
>>> if a:
...   print "yes"
... else:
...   print "no"
...
no
4.1.3 成員運算符
操做符
描述
in
在對象裏
not in 
不在對象裏
示例:
>>> 'a' in 'abc'
True
>>> 'd' in 'abc'
False
>>> lst = ['a','b','c']
>>> 'a' in lst
True
>>> 'd' in lst
False
 
>>> 'a' not in 'abc'
False
>>> 'd' not in 'abc'   
True
>>> 'd' not in lst
True
4.1.4 標識運算符
操做符
描述
is
內存地址相等
is not 
內存地址不相等
示例:
>>> a = []
>>> b = []
>>> id(a)
139741563903296
>>> id(b)
139741563902144
>>> a is b
False
>>> a is not b
True
這裏用到了id()函數,用於獲取對象在內存的地址。
4.2 條件判斷
   4.2.1 單分支
>>> a = 20
>>> if a < 18: 
...   print "no"
... else:
...   print "yes"
...
yes
   有時候一個簡單的判斷語句,感受這樣寫麻煩,有沒有一條命令搞定的。
   有的,簡寫if語句:
>>> a = 20
>>> result = ("yes" if a == 20 else "no")
>>> result
'yes'
>>> type(result)
<type 'str'>
 
# 有時會看到別人代碼用中括號,意思把結果存儲爲一個列表
>>> result = ["yes" if a == 20 else "no"]
>>> result
['yes']
>>> type(result)
<type 'list'>
   4.2.2 多分支
>>> a = 20
>>> if a < 18:
...   print "no"
... elif a == 20:
...   print "yes"
... else:
...   print "other"
...
yes
   4.2.3 pass語句
     >>> a = 20
>>> if a < 18:
...   print "no"
... elif a == 20:
...   pass
... else:
...   print "other"
...
   pass語句做用是不執行當前代碼塊,與shell中的冒號作做用同樣。
 
博客地址:http://lizhenliang.blog.51cto.com and https://yq.aliyun.com/u/lizhenliang
QQ羣:323779636(Shell/Python運維開發羣)
 
4.3 循環語句
   4.3.1 for
     1)迭代對象
       遍歷字符串,每一個字符當作單個遍歷:
>>> for i in "abc":
...   print i
...
a
b
c
       使用range()函數生成一個數字序列列表,並遍歷:
>>> for i in range(1,5):
...   print i
...
1
2
3
4
       回顧下第三章講的遍歷字典:
>>> d = {'a':1, 'b':2, 'c':3}
>>> for i in d.iteritems():
...   print "%s:%s" %(i[0],i[1])
...
a:1
c:3
b:2
      2)嵌套循環
        逐個循環判斷外層列表裏元素是否存在內層列表:
>>> for i in range(1,6):
...   for x in range(3,8):
...     if i == x:
...       print i
...
3
4
5
      3)簡寫語句
        簡寫for語句:
>>> result = (x for x in range(5))
>>> result
<generator object <genexpr> at 0x030A4FD0>
>>> type(result)
<type 'generator'>
說明:在這裏用小括號,會生成一個生成器,在這裏知道下就能夠了,不過多講解,後面會專門生成器用途。
 
# 一樣用中括號會以列表存儲
>>> result = [ x for x in range(5)]
>>> type(result)
<type 'list'>
>>> result
[0, 1, 2, 3, 4]
       for和if語句寫一行:
>>> result = [ x for x in range(5) if x % 2 == 0]
>>> result
[0, 2, 4]
   4.3.2 while
    語法:
    while 表達式:
        執行語句...
       1)輸出序列
         當條件知足時,中止循環:
>>> while count < 5:
...   print count   
...   count += 1   
...
0
1
2
3
4
       2)死循環
>>> import time
>>> i = 1
>>> while True:     
...   print i       
...   i += 1         
...   time.sleep(0.5)
...
1
2
3
......   # 會一直循環,直到海枯石爛,天荒地老...
       注意:當表達式值爲true或者非零時,都會一直循環。
   4.3.3 continue和break語句
   continue當知足條件時,跳出本次循環。
   break當知足條件時,跳出全部循環。
   for和while用法同樣。
      1)基本使用
       知足條件跳出當前循環:
#!/usr/bin/env python
for i in range(1,6):
    if i == 3:
        continue
    else:
        print i
# python test.py
1
2
4
5
 
#!/usr/bin/env python
count = 0
while count < 5:
    count += 1
    if count == 3:
        continue
    else:
        print count
# python test.py
1
2
4
5
       知足條件終止循環:
#!/usr/bin/env python
for i in range(1,6):
    if i == 3:
        break
    else:
        print i
# python test.py
1
2
 
#!/usr/bin/env python
count = 0
while count < 5:
    count += 1
    if count == 3:
        break
    else:
        print count
# python test.py
1
2
      2)輸入錯誤次數超過三次退出
       例如:提示用戶輸入名字,若是名字是xiaoming輸入正確退出,不然一直提示從新輸入,直到三次退出。
#!/usr/bin/env python
count = 0
while 1:
    if count < 3:
        name = raw_input("Please input your name: ").strip()    # .strip()去除首尾空格
        if len(name) == 0:
            print "Input can not be empty!"
            count += 1
            continue
        elif name == "xiaoming":
            print "OK."
            break
        else:
            print "Name input error, please input again!"
            count += 1
    else:
        print "Error three times, Exit!"
        break
   4.3.4 else語句
      else語句會在循環正常執行完才執行。在for循環用法也同樣。
>>> count = 0   
>>> while count < 5:
...   print count
...   count += 1
... else:
...   print "end"
...
0
1
2
3
4
end
 
>>> count = 0       
>>> while count < 5:
...   print count   
...   break
... else:
...   print "end"
...
0

第五章 Python函數你知多少

函數做用:把一些複雜的代碼封裝起來,函數通常都是一個功能,用的時候才調用,提升重複利用率和簡化程序結構。
5.1 語法
def functionName(parms1, parms2, ...):
     code block
     return expression
函數以def關鍵字開頭,空格後跟函數名,括號裏面是參數,用於傳參,函數代碼段裏面引用。
5.2 函數定義與調用
# 定義函數
>>> def func():
...   print "Hello world!"
...   return "Hello world!" 
...
# 調用函數
>>> func()
Hello world!
'Hello world!'
當咱們定義好函數,是不執行的,沒有任何輸出。當輸入函數名後跟雙小括號纔會執行函數裏寫的代碼。
順便說下print和return區別:
有沒有點奇怪!爲何print和return輸出同樣呢,return就加個單引號,貌似也沒啥明顯區別啊!其實在解釋器下全部的結果都會輸出的。
先了解下return做用:結束函數,並返回一個值。若是不跟表達式,會返回一個None。
好,那麼咱們深刻了解下他們區別,舉個例子,寫個py程序:
#!/usr/bin/env python
def func():
    print "1: Hello world!"
    return "2: Hello world!"
func()
# python test.py
1: Hello world!
明白點了嘛?print是打印對象的值,而return是返回對象的值。也就是說你return默認是將對象值存儲起來,要想知道里面的值,能夠用print能夠打印。
#!/usr/bin/env python
def func():
    print "1: Hello world!"
    return "2: Hello world!"
print func()
# python test.py
1: Hello world!
2: Hello world!
爲何函數裏面不用print就在這裏,每每咱們定義一個函數是不須要打印的,而是交給其餘代碼去處理這個函數返回值。固然,print在調試函數代碼時會起到很好的幫助。
5.3 函數參數
   5.3.1 接受參數
>>> def func(a, b):
...   print a + b
...
>>> func(1, 2)
3
>>> func(1, 2, 3)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: func() takes exactly 2 arguments (3 given)
   a和b能夠理解爲是個變量,可由裏面代碼塊引用。調用函數時,小括號裏面的表達式數量要對應函數參數數量,而且按傳參按位置賦予函數參數位置。若是數量不對應,會拋出TypeError錯誤。
   固然,函數參數也能夠是數組:
>>> def func(a):
...   print a
...
>>> func([1,2,3])
[1, 2, 3]
>>> func({'a':1,'b':2})
{'a': 1, 'b': 2}
   若是不想一一對應傳參,能夠指定參數值:
>>> def func(a,b):
...   print a + b
...
>>> func(b=2,a=1)
3
   5.3.2 函數參數默認值
   參數默認值是預先定義好,若是調用函數時傳入了這個值,那麼將以傳入的爲實際值,不然是默認值。
>>> def func(a, b=2):
...   print a + b
...
>>> func(1)
3
>>> func(1, 3)
4
   5.3.3 接受任意數量參數
   上面方式固定了參數多個,當不知道多少參數時候能夠用如下方式。
   單個星號使用:
>>> def func(*a):     
...   print a
...
>>> func(1,2,3)
(1, 2, 3)
   單個星號存儲爲一個元組。
   兩個星號使用:
>>> def func(**a):
...   print a
...
>>> func(a=1, b=2, c=3)
{'a': 1, 'c': 3, 'b': 2}
   兩個星號存儲爲一個字典。可見它們都是以數組的形式傳入。
   你也許在查資料的時候,會看到這樣寫的函數參數(*args, **kwargs),與上面只是名字不同罷了 :
>>> def func(*args, **kwargs):
...   print args
...   print kwargs
...
>>> func(1,2,3,a=1,b=2,c=3)
(1, 2, 3)
{'a': 1, 'c': 3, 'b': 2}
   與普通參數一塊兒使用:
>>> def func(a, b, *c):
...   print a + b
...   print c
...
>>> func(1,2,3,5,6)
3
(3, 5, 6)
 
>>> def func(a, b, **c):
...   print a + b
...   print c
...
>>> func(1,2,a=1,b=2,c=3)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: func() got multiple values for keyword argument 'a'
>>> func(1,2,c=3,d=4,e=5)
3
{'c': 3, 'e': 5, 'd': 4}
   拋出異常,是由於傳入的第一個參數1,和第三個參數a=1,都認爲是傳入函數參數a了。請注意下這點。
5.4 做用域
做用域聽着挺新鮮,其實很簡單,就是限制一個變量或一段代碼可用範圍,不在這個範圍就不可用。提升了程序邏輯的局部性,減小名字衝突。
做用域範圍通常是:全局(global)->局部(local)->內置(build-in)
先看看全局和局部變量:
>>> a = 2
>>> def func():
...   b = 3
...
>>> a
2
>>> b
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'b' is not defined
a變量的做用域是整個代碼中有效,稱爲全局變量,也就是說一段代碼最開始定義的變量。
b變量的做用域在函數內部,也就是局部變量,在函數外是不可引用的。
這麼一來,全局變量與局部變量即便名字同樣也不衝突。
若是函數內部的變量也能在全局引用,須要使用global聲明:
>>> def func():
...   global b
...   b = 3
...
>>> b
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'b' is not defined
>>> func()
>>> b
3
拋出異常,說明一個問題,當函數沒引用使用,裏面的代碼塊是沒有解釋的。
使用global聲明變量後外部是能夠調用函數內部的變量的。
5.5 嵌套函數
# 不帶參數
>>> def func():
...   x = 2
...   def func2():
...     return x
...   return func2  # 返回func2函數
...
>>> func()()
2
>>> func2()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'func2' is not defined
 
>>> def func():   
...   x = 2         
...   global func2
...   def func2():
...     return x 
...   return func2
...
>>> func()()
2
>>> func2()
2
內層函數能夠訪問外層函數的做用域。內嵌函數只能被外層函數調用,但也能夠使用global聲明全局做用域。
調用內部函數的另外一種用法:
# 帶參數
>>> def func(a):
...   def func2(b):
...     return a * b
...   return func2
...
>>> f = func(2)   # 變量指向函數。是的,變量能夠指向函數。
>>> f(5)
10
>>> func(2)(5)
10
內層函數能夠訪問外層函數的做用域 。但 變量不能從新賦值,舉例說明:
>>> def func():
...   x = 2
...   def func2():
...      x = 3
...   func2()
...   return x
...
>>> func()
2
 
>>> def func():
...   x = 2
...   def func2():
...     x += 1
...   func2()
...   return x
...
>>> func()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 5, in func
  File "<stdin>", line 4, in func2
UnboundLocalError: local variable 'x' referenced before assignment
5.6 閉包
「官方」的解釋是:所謂「閉包」,指的是一個擁有許多變量和綁定了這些變量的環境的表達式(一般是一個函數),於是這些變量也是該表達式的一部分。
其實,上面嵌套函數就是閉包一種方式:
>>> def func(a):
...   def func2(b):
...     return a * b
...   return func2
...
>>> f = func(2)   # 變量指向函數。是的,變量能夠指向函數。
>>> f(5)
10
func是一個函數,裏面又嵌套了一個函數func2,外部函數傳過來的a參數,這個變量會綁定到函數func2。func函數之內層函數func2做爲返回值,而後把func函數存儲到f變量中。當外層函數調用內層函數時,內層函數纔會執行(func()()),就建立了一個閉包。
5.7 高階函數
高階函數是至少知足這兩個任意中的一個條件:
1) 能接受一個或多個函數做爲輸入。
2)輸出一個函數。
abs、map、reduce都是高階函數,後面會講解。
其實,上面所講的嵌套函數也是高階函數。
舉例說明下高階函數:
>>> def f(x):
...   return x * x
...
>>> def f2(func, y):
...   return func(y)
...
>>> f2(f, 2)
4
這裏的f2就是一個高階函數,由於它的第一個參數是一個函數,知足了第一個條件。
 
博客地址:http://lizhenliang.blog.51cto.com and https://yq.aliyun.com/u/lizhenliang
QQ羣:323779636(Shell/Python運維開發羣)
 
5.8 函數裝飾器
裝飾器(decorator)自己是一個函數,包裝另外一個函數或類,它可讓其餘函數在不須要改動代碼狀況下動態增長功能,裝飾器返回的也是一個函數對象。
先舉一個例子,說明下裝飾器的效果,定義兩個函數,分別傳參計算乘積:
#!/usr/bin/python
# -*- coding: utf-8 -*-
def f1(a, b):
    print "f1 result: " + str(a * b)
def f2(a, b):
    print "f2 result: " + str(a * b)
 
f1(1, 2)
f2(2, 2)
 
# python test.py
f1 result: 2
f2 result: 4
跟預期的那樣,打印出了乘積。
若是我想給這兩個函數加一個打印傳入的參數,怎麼辦,應該這樣:
#!/usr/bin/python
# -*- coding: utf-8 -*-
def f1(a, b):
    print "f1 parameter: %d %d" %(a, b)
    print "f1 result: " + str(a * b)
def f2(a, b):
    print "f2 parameter: %d %d" %(a, b)
    print "f2 result: " + str(a * b)
 
f1(1, 2)
f2(2, 2)
 
# python test.py
f1 parameter: 1 2
f1 result: 2
f2 parameter: 2 2
f2 result: 4
按照所想的打印了傳入的參數,有沒有方法能更簡潔點呢,來看看裝飾器後的效果。
#!/usr/bin/python
# -*- coding: utf-8 -*-
def deco(func):
    def f(a, b):
        print "%s parameter: %d %d" %(func.__name__, a, b)
        return func(a, b)
    return f
 
@deco
def f1(a, b):
    print "f1 result: " + str(a * b)
@deco
def f2(a, b):
    print "f2 result: " + str(a * b)
 
f1(1, 2)
f2(2, 2)
 
# python test.py
f1 parameter: 1 2
f1 result: 2
f2 parameter: 2 2
f2 result: 4
可見用裝飾器也實現了上面方法,給要裝飾的函數添加了裝飾器定義的功能,這種方式顯得是否是更簡潔呢!
好,那麼咱們繼續深刻學習裝飾器用法。
   5.8.1 無參數裝飾器
方式1:函裝飾器函數裝飾函數
#!/usr/bin/python
# -*- coding: utf-8 -*-
def deco(func):
    return func
def f1():
    print "Hello world!"
myfunc = deco(f1)
myfunc()  
# python test.py
Hello world!
 
方式2:使用語法糖"@"來裝飾函數
#!/usr/bin/python
# -*- coding: utf-8 -*-
def deco(func):
    return func
@deco
def f1():
    print "Hello world!"
f1()
# python test.py
Hello world!
   方式1是將一個函數做爲參數傳給裝飾器函數。
   方式2使用了語法糖,也實現一樣效果。
   其實兩種方式結果同樣,方式1須要每次使用裝飾器時要先變量賦值下,而方式2使用裝飾器時直接用語法糖"@"引用,會顯得更方便些,實際代碼中通常也都是用語法糖。
   5.8.2 帶參數裝飾器
#!/usr/bin/python
# -*- coding: utf-8 -*-
def deco(func):
    def f(a, b):
        print "function name: %s" % func.__name__   # __name__屬性是獲取函數名,爲了說明執行了這個函數
        return func(a, b)   # 用接受過來的func函數來處理傳過來的參數
    return f
 
@deco
def f1(a, b):
    print "Hello world!"
    print a + b
f1(2, 2)
 
# python test.py
function name: f1
Hello world!
4
   3)不固定參數
#!/usr/bin/python
# -*- coding: utf-8 -*-
def log(func):
    def deco(*args, **kwargs):
        print "function name: %s" % func.__name__
        return func(*args, **kwargs)
    return deco
 
@log
def f1(a, b):
    print "f1() run."
    print a + b
f1(1,2)
 
# python test.py
function name: f1
f1() run.
3
   4)裝飾器加參數
#!/usr/bin/python
# -*- coding: utf-8 -*-
# 三層函數,調用log函數返回deco函數,再調用返回的函數deco,則返回值是_deco函數
def log(arg):
    def deco(func):
        def _deco(*args, **kwargs):
            print "%s - function name: %s" % (arg, func.__name__)  
            return func(*args, **kwargs)
        return _deco
    return deco
 
@log("info")
def f1(a, b):
    print "f1() run."
    print a + b
f1(1,2)
 
# python test.py
info - function name: f1
f1() run.
3
   再舉一個例子,給函數輸出字符串帶顏色:
#!/usr/bin/python
# -*- coding: utf-8 -*-
 
def fontColor(color):
    begin = "\033["
    end = "\033[0m"
 
    d = {
        'red':'31m',
        'green':'32m',
        'yellow':'33m',
        'blue':'34m'
    }
    def deco(func):
        print begin + d[color] + func() + end
    return deco
 
@fontColor("red")
def f():
    return "Hello world!"
 
@fontColor("green")
def f2():
    return "Hello world!"
   能夠看出裝飾器處理方式知足了高階函數的條件,因此裝飾器也是一種高階函數。
   裝飾器優勢:靈活給裝飾器增長功能,而不修改函數,提升代碼可重複利用性,增長可讀性。
5.9 匿名函數
匿名函數:定義函數的一種形式,無需定義函數名和語句塊,所以代碼邏輯會受到侷限,同時也減小代碼量,增長可讀性。
在Python中匿名函數是lambda。
舉例子說明def關鍵字與lambda函數定義函數區別:
# 普通函數
>>> def func():
...   return "Hello world!"
...
>>> func()
 
>>> def func(a, b):
...   return a * b
...
>>> func(2, 2)
4
# 匿名函數
>>> f = lambda:"Hello world!"
>>> f()
'Hello world!'
 
>>> f = lambda a, b: a * b   # 冒號左邊是函數參數,右邊是返回值
>>> f(2, 2)
4
lambda函數一行就寫成一個函數功能,省去定義函數過程,讓代碼更加精簡。
5.10 內置高階函數
   5.10.1 map()
   語法:map(function, sequence[, sequence, ...]) -> list
   將序列中的元素經過函數處理返回一個新列表。
   例如:
>>> lst = [1,2,3,4,5]
>>> map(lambda x:str(x)+".txt", lst)
['1.txt', '2.txt', '3.txt', '4.txt', '5.txt']
   5.10.2 filter()
   語法:filter(function or None, sequence) -> list, tuple, or string
   將序列中的元素經過函數處理返回一個新列表、元組或字符串。
   例如:過濾列表中的奇數
>>> lst = [1,2,3,4,5]
>>> filter(lambda x:x%2==0, lst)
[2, 4]
   5.10.3 reduce()
   語法:reduce(function, sequence[, initial]) -> value
   reduce()是一個二元運算函數,因此只接受二元操做函數。
   例如:計算列表總和
>>> lst = [1,2,3,4,5]
>>> reduce(lambda x,y:x+y, lst)
15
   先將前兩個元素相加等於3,再把結果與第三個元素相加等於6,以此類推。這就是reduce()函數功能。

第六章 Python類(面向對象編程)

什麼是面向對象編程?
   面向對象編程(Object Oriented Programming,OOP,面向對象程序設計)是一種計算機編程架構。Python就是這種編程語言。
   面向對象程序設計中的概念主要包括:對象、類、繼承、動態綁定、封裝、多態性、消息傳遞、方法。
   1)對象:類的實體,好比一我的。
   2)類:一個共享相同結構和行爲的對象的集合。通俗的講就是分類,好比人是一類,動物是一類。
   3)繼承:類之間的關係,好比貓狗是一類,他們都有四條腿,狗繼承了這個四條腿,擁有了這個屬性。
   4)動態綁定:在不修改源碼狀況下,動態綁定方法來給實例增長功能。
   5)封裝:把相同功能的類方法、屬性封裝到類中,好比人兩條腿走路,狗有四條腿走路,兩個不能封裝到一個類中。
   6)多態性:一個功能能夠表示不一樣類的對象,任何對象能夠有不一樣的方式操做。好比一個狗會走路、會跑。
   7)消息傳遞:一個對象調用了另外一個對象的方法。
   8)方法:類裏面的函數,也稱爲成員函數。
   對象=屬性+方法。
   屬性:變量。
   方法:函數。
   實例化:建立一個類的具體實例對象。好比一條泰迪。
什麼是類?
   類是對對象的抽象,對象是類的實體,是一種數據類型。它不存在內存中,不能被直接操做,只有被實例化對象時,纔會變的可操做。
   類是對現實生活中一類具備共同特徵的事物的抽象描述。
6.1 類和類方法語法
# 類
class ClassName():
    pass
# 類中的方法
def funcName(self):
       pass
self表明類自己。類中的全部的函數的第一個參數必須是self。
6.2 類定義與調用
#!/usr/bin/python
# -*- coding: utf-8 -*-
class MyClass():
    x = 100
    def func(self, name):
        return "Hello %s!" % name
    def func2(self):
        return self.x
mc = MyClass()  # 類實例化,綁定到變量mc
print mc.x   # 類屬性引用
print mc.func("xiaoming")  # 調用類方法
print mc.func2()
 
# python test.py
100
Hello xiaoming!
100
上面示例中,x變量稱爲類屬性,類屬性又分爲類屬性和實例屬性:
   1)類屬性屬於類自己,經過類名訪問,通常做爲全局變量。好比mc.x
   2)若是類方法想調用類屬性,須要使用self關鍵字調用。好比self.x
   3)實例屬性是實例化後對象的方法和屬性,經過實例訪問,通常做爲局部變量。下面會講到。
   4)當實例化後能夠動態類屬性,下面會講到。
類方法調用:
1)類方法之間調用:self.<方法名>(參數),參數不須要加self
2)外部調用:<實例名>.<方法名>
6.3 類的說明
給類添加註釋,提升可閱讀性,可經過下面方式查看。
方法1:
>>> class MyClass:
...   """
...   這是一個測試類.
...   """
...   pass
...
>>> print MyClass.__doc__
 
  這是一個測試類.
 
>>>
方法2:
>>> help(MyClass)
Help on class MyClass in module __main__:
 
class MyClass
 |  這是一個測試類.
6.4 類內置方法
內置方法
描述
__init__(self, ...)
初始化對象,在建立新對象時調用
__del__(self)
釋放對象,在對象被刪除以前調用
__new__(cls, *args, **kwd)
實例的生成操做,在__init__(self)以前調用
__str__(self)
在使用print語句時被調用,返回一個字符串
__getitem__(self, key)
獲取序列的索引key對應的值,等價於seq[key]
__len__(self)
在調用內建函數len()時被調用
__cmp__(str, dst)
比較兩個對象src和dst
__getattr__(s, name)
獲取屬性的值
__setattr__(s, name, value)
設置屬性的值
__delattr__(s, name)
刪除屬性
__gt__(self, other)
判斷self對象是否大於other對象
__lt__(self, other)
判斷self對象是否小於other對象
__ge__(self, other)
判斷self對象是否大於或等於other對象
__le__(self, other)
判斷self對象是否小於或等於other對象
__eq__(self, other)
判斷self對象是否等於other對象
__call__(self, *args)
把實例對象做爲函數調用
6.5 初始化實例屬性
   不少類通常都有初始狀態的,經常定義對象的共同特性,也能夠用來定義一些你但願的初始值。
   Python類中定義了一個構造函數__init__,對類中的實例定義一個初始化對象,經常使用於初始化類變量。當類被實例化,第二步自動調用的函數,第一步是__new__函數。
   __init__構造函數也可讓類傳參,相似於函數的參數。
   __init__構造函數使用:
#!/usr/bin/python
# -*- coding: utf-8 -*-
class MyClass():
    def __init__(self):
        self.name = "xiaoming"
    def func(self):
        return self.name
 
mc = MyClass()
print mc.func()
 
# python test.py
xiaoming
   __init__函數定義到類的開頭.self.name變量是一個實例屬性,只能在類方法中使用,引用時也要這樣self.name。
   類傳參:
#!/usr/bin/python
# -*- coding: utf-8 -*-
class MyClass():
    def __init__(self, name):
        self.name = name
    def func(self, age):
        return "name: %s,age: %s" %(self.name, age)
 
mc = MyClass('xiaoming')  # 第一個參數是默認定義好的傳入到了__init__函數
print mc.func('22') 
 
# python test.py
Name: xiaoming, Age: 22
6.6 類私有化(私有屬性)
   6.6.1 單下劃線
   實現模塊級別的私有化,以單下劃線開頭的變量和函數只能類或子類才能訪問。當from modulename import * 時將不會引入以單下劃線卡頭的變量和函數。
#!/usr/bin/python
# -*- coding: utf-8 -*-
class MyClass():
    _age = 21
    def __init__(self, name=None):
        self._name = name
    def func(self, age):
        return "Name: %s, Age: %s" %(self._name, age)
 
mc = MyClass('xiaoming')
print mc.func('22')
print mc._name
print mc._age
 
# python test.py
Name: xiaoming, Age: 22
xiaoming
21
   _age和self._name變量其實就是作了個聲明,說明這是個內部變量,外部不要去引用它。
   6.6.2 雙下劃線
   以雙下劃線開頭的變量,表示私有變量,受保護的,只能類自己能訪問,連子類也不能訪問。避免子類與父類同名屬性衝突。
#!/usr/bin/python
# -*- coding: utf-8 -*-
class MyClass():
      __age = 21
      def __init__(self, name=None):
          self.__name = name
      def func(self, age):
          return "Name: %s, Age: %s" %(self.__name, age)
 
mc = MyClass('xiaoming')
print mc.func('22')
print mc.__name
print mc.__age
 
# python test.py
Name: xiaoming, Age: 22
Traceback (most recent call last):
  File "test.py", line 12, in <module>
    print mc.__name
AttributeError: MyClass instance has no attribute '__name'
   可見,在單下劃線基礎上又加了一個下劃線,一樣方式類屬性引用,出現報錯。說明雙下劃線變量只能自己能用。
   若是想訪問私有變量,能夠這樣:
#!/usr/bin/python
# -*- coding: utf-8 -*-
class MyClass():
    __age = 21
    def __init__(self, name=None):
        self.__name = name
    def func(self, age):
        return "Name: %s, Age: %s" %(self.__name, age)
 
mc = MyClass('xiaoming')
print mc.func('22')
print mc._MyClass__name
print mc._MyClass__age
 
# python test.py
Name: xiaoming, Age: 22
xiaoming
21
   self.__name變量編譯成了self._MyClass__name,以達到不能被外部訪問的目的,並無真正意義上的私有。
   6.6.3 特殊屬性(首尾雙下劃線)
   通常保存對象的元數據,好比__doc__、__module__、__name__:
>>> class MyClass:
    """
    這是一個測試類說明的類。
    """
    pass
 
# dic()返回對象內變量、方法
>>> dir(MyClass)
['__doc__', '__module__']
 
>>> MyClass.__doc__
'\n\t\xd5\xe2\xca\xc7\xd2\xbb\xb8\xf6\xb2\xe2\xca\xd4\xc0\xe0\xcb\xb5\xc3\xf7\xb5\xc4\xc0\xe0\xa1\xa3\n\t'
>>> MyClass.__module__
'__main__'
>>> MyClass.__name__
'MyClass'
   這裏用到了一個新內置函數dir(),不帶參數時,返回當前範圍內的變量、方法的列表。帶參數時,返回參數的屬性、方法的列表。
Python本身調用的,而不是用戶來調用。像__init__ ,你能夠重寫。

博客地址:http://lizhenliang.blog.51cto.com and https://yq.aliyun.com/u/lizhenlianghtml

QQ羣:323779636(Shell/Python運維開發羣)
前端


6.7 類的繼承
子類繼承父類,子類將繼承父類的全部方法和屬性,提升代碼重用。
   1)簡單繼承
#!/usr/bin/python
# -*- coding: utf-8 -*-
class Parent():
    def __init__(self, name=None):
        self.name = name
    def func(self, age):
        return "Name: %s, Age: %s" %(self.name, age)
class Child(Parent):
    pass
 
mc = Child('xiaoming')
print mc.func('22')
print mc.name
 
# python test.py
Name: xiaoming, Age: 22
xiaoming
   2)子類實例初始化
   若是子類重寫了構造函數,那麼父類的構造函數將不會執行:
#!/usr/bin/python
# -*- coding: utf-8 -*-
class Parent():
    def __init__(self):
        self.name_a = "xiaoming"
    def funcA(self):
        return "function A: %s" % self.name_a
class Child(Parent):
    def __init__(self):
        self.name_b = "zhangsan"
    def funcB(self):
        return "function B: %s" % self.name_b
 
mc = Child()
print mc.name_b
print mc.funcB()
print mc.funcA()
 
# python test.py
zhangsan
function B: zhangsan
Traceback (most recent call last):
  File "test2.py", line 17, in <module>
    print mc.funcA()
  File "test2.py", line 7, in funcA
    return "function A: %s" % self.name_a
AttributeError: Child instance has no attribute 'name_a'
   拋出錯誤,提示調用funcA()函數時,沒有找到name_a屬性,也就說明了父類的構造函數並無執行。
   若是想解決這個問題,可經過下面兩種方法:
   方法1:調用父類構造函數
#!/usr/bin/python
# -*- coding: utf-8 -*-
class Parent():
    def __init__(self):
        self.name_a = "xiaoming"
    def funcA(self):
        return "function A: %s" % self.name_a
class Child(Parent):
    def __init__(self):
        Parent.__init__(self)
        self.name_b = "zhangsan"
    def funcB(self):
        return "function B: %s" % self.name_b
 
mc = Child()
print mc.name_b
print mc.funcB()
print mc.funcA()
 
# python test.py
zhangsan
function B: zhangsan
function A: xiaoming
   方法2:使用supper()函數繼承
#!/usr/bin/python
# -*- coding: utf-8 -*-
class Parent(object):
    def __init__(self):
        self.name_a = "xiaoming"
    def funcA(self):
        return "function A: %s" % self.name_a
class Child(Parent):
    def __init__(self):
        super(Child, self).__init__()
        self.name_b = "zhangsan"
    def funcB(self):
        return "function B: %s" % self.name_b
 
mc = Child()
print mc.name_b
print mc.funcB()
print mc.funcA()
 
# python test.py
zhangsan
function B: zhangsan
function A: xiaoming
6.8 多重繼承
每一個類能夠擁有多個父類,若是調用的屬性或方法在子類中沒有,就會從父類中查找。多重繼承中,是依次按順序執行。
類簡單的繼承:
#!/usr/bin/python
# -*- coding: utf-8 -*-
class A:
    def __init__(self):
        self.var1 = "var1"
        self.var2 = "var2"
    def a(self):
        print "a..."
class B:
    def b(self):
        print "b..."
class C(A,B):
    pass
 
c = C()
c.a()
c.b()
print c.var1
print c.var2
 
# python test.py
a...
b...
var1
var2
類C繼承了A和B的屬性和方法,就能夠像使用父類同樣使用它。
子類擴展方法,直接在子類中定義便可:
#!/usr/bin/python
# -*- coding: utf-8 -*-
class A:
    def __init__(self):
        self.var1 = "var1"
        self.var2 = "var2"
    def a(self):
        print "a..."
class B:
    def b(self):
        print "b..."
class C(A,B):
    def test(self):
        print "test..."
 
c = C()
c.a()
c.b()
c.test()
print c.var1
print c.var2
 
# python test.py
a...
b...
test...
var1
var2
在這說明下經典類和新式類。
經典類:默認沒有父類,也就是沒繼承類。
新式類:有繼承的類,若是沒有,能夠繼承object。在Python3中已經默認繼承object類。
經典類在多重繼承時,採用從左到右深度優先原則匹配,而新式類是採用C3算法(不一樣於廣度優先)進行匹配。二者主要區別在於遍歷父類算法不一樣,具體些請在網上查資料。
6.9 方法重載
直接定義和父類同名的方法,子類就修改了父類的動做。
#!/usr/bin/python
# -*- coding: utf-8 -*-
class Parent():
    def __init__(self, name='xiaoming'):
        self.name = name
    def func(self, age):
        return "Name: %s, Age: %s" %(self.name, age)
class Child(Parent):
    def func(self, age=22):
        return "Name: %s, Age: %s" %(self.name, age)
 
mc = Child()
print mc.func()
 
# python test.py
Name: xiaoming, Age: 22
6.10 修改父類方法
在方法重載中調用父類的方法,實現添加功能。
#!/usr/bin/python
# -*- coding: utf-8 -*-
class Parent():
    def __init__(self, name='xiaoming'):
        self.name = name
    def func(self, age):
        return "Name: %s, Age: %s" %(self.name, age)
class Child(Parent):
    def func(self, age):
        print "------"
        print Parent.func(self, age)   # 調用父類方法
        print "------"
 
mc = Child()
mc.func('22')
 
# python test.py
------
Name: xiaoming, Age: 22
------
還有一種方式經過super函數調用父類方法:
#!/usr/bin/python
# -*- coding: utf-8 -*-
class Parent():
    def __init__(self, name='xiaoming'):
        self.name = name
    def func(self, age):
        return "Name: %s, Age: %s" %(self.name, age)
class Child(Parent):
    def func(self, age):
        print "------"
        print super(Child, self).func(age)
        print "------"
 
mc = Child()
mc.func('22')
 
# python test.py
------
Traceback (most recent call last):
  File "test2.py", line 15, in <module>
    mc.func('22')
  File "test2.py", line 11, in func
    print super(Child, self).func(age)
TypeError: must be type, not classobj
拋出錯誤,由於super繼承只能用於新式類,用於經典類就會報錯。
那咱們就讓父類繼承object就能夠使用super函數了:
#!/usr/bin/python
# -*- coding: utf-8 -*-
class Parent(object):
    def __init__(self, name='xiaoming'):
        self.name = name
    def func(self, age):
        return "Name: %s, Age: %s" %(self.name, age)
class Child(Parent):
    def func(self, age):
        print "------"
        print super(Child, self).func(age)   # 調用父類方法。在Python3中super參數可不用寫。
        print "------"
 
mc = Child()
mc.func('22')
 
# python test.py
------
Name: xiaoming, Age: 22
------
6.11 屬性訪問的特殊方法
有四個可對類對象增刪改查的內建函數,分別是getattr()、hasattr()、setattr()、delattr()。
   6.11.1 getattr()
   返回一個對象屬性或方法。
>>> class A:
...   def __init__(self):
...     self.name = 'xiaoming'
...   def method(self):
...     print "method..."
...
>>> c = A()
>>> getattr(c, 'name', 'Not find name!')   
'xiaoming'
>>> getattr(c, 'namea', 'Not find name!')
>>> getattr(c, 'method', 'Not find method!')
<bound method A.method of <__main__.A instance at 0x93fa70>>
>>> getattr(c, 'methoda', 'Not find method!')
'Not find method!'
   6.11.2 hasattr()
   判斷一個對象是否具備屬性或方法。返回一個布爾值。
>>> hasattr(c, 'name')
True
>>> hasattr(c, 'namea')
False
>>> hasattr(c, 'method')
True
>>> hasattr(c, 'methoda')
False
   6.11.3 setattr()
   給對象屬性從新賦值或添加。若是屬性不存在則添加,不然從新賦值。
>>> hasattr(c, 'age')
False
>>> setattr(c, 'age', 22)
>>> c.age
22
>>> hasattr(c, 'age')
True
   6.11.4 delattr()
   刪除對象屬性。
>>> delattr(c, 'age')
>>> hasattr(c, 'age')             
False
6.12 類裝飾器
與函數裝飾器相似,不一樣的是類要當作函數同樣調用:
#!/usr/bin/python
# -*- coding: utf-8 -*-
class Deco:
    def __init__(self, func):
       self._func = func
       self._func_name = func.__name__
    def __call__(self):
       return self._func(), self._func_name
 
@Deco
def f1():
    return "Hello world!"
 
print f1()
 
# python test.py
('Hello world!', 'f1')
6.13 類內置裝飾器
   下面介紹類函數裝飾器,在實際開發中,感受不是很經常使用。
   6.10.1 @property
   @property屬性裝飾器的基本功能是把類中的方法當作屬性來訪問。
   在沒使用屬性裝飾器時,類方法是這樣被調用的:
>>> class A:
...    def __init__(self, a, b):
...      self.a = a
...      self.b = b
...    def func(self):
...      print self.a + self.b
...
>>> c = A(2,2)
>>> c.func()
4
>>> c.func
<bound method A.func of <__main__.A instance at 0x7f6d962b1878>>
   使用屬性裝飾器就能夠像屬性那樣訪問了:
>>> class A:
...     def __init__(self, a, b):
...       self.a = a
...       self.b = b
...     @property
...     def func(self):
...       print self.a + self.b
...
>>> c = A(2,2)
>>> c.func
4
>>> c.func()
4
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'NoneType' object is not callable
   6.10.2 @staticmethod
   @staticmethod是靜態方法裝飾器,能夠經過類對象訪問,也能夠經過實例化後類對象實例訪問。
   實例方法的第一個參數是self,表示是該類的一個實例,稱爲類對象實例。
   而使用靜態方法裝飾器,第一個參數就不用傳入實例自己(self),那麼這個方法當作類對象,由Python自身處理。
   看看普通方法的用法:
>>> class A:                     
...   def staticMethod(self):   
...      print "not static method..."
...
>>> c = A()         
>>> c.staticMethod()
not static method...
   使用靜態方法則是這麼用:
>>> class A:                   
...   @staticmethod             
...   def staticMethod():       
...     print "static method..."
...
>>> A.staticMethod()   # 能夠經過類調用靜態方法
static method...
>>> c = A()   
>>> c.staticMethod()   # 還能夠使用普通方法調用
static method...
    靜態方法和普通的非類方法做用同樣,只不過命名空間是在類裏面,必須經過類來調用。通常與類相關的操做使用靜態方法。
   6.10.3 @classmethod
   @classmethod是類方法裝飾器,與靜態方法裝飾器相似,也能夠經過類對象訪問。主要區別在於類方法的第一個參數要傳入類對象(cls)。
>>> class A:                   
...   @classmethod             
...   def classMethod(cls):   
...     print "class method..."
...     print cls.__name__
...
>>> A.classMethod()
class method...
A
6.14 __call__方法
可讓類中的方法像函數同樣調用。
>>> class A:
...   def __call__(self, x): 
...     print "call..."
...     print x
...
>>> c = A()
>>> c(123)
call...
123
 
>>> class A:
...   def __call__(self, *args, **kwargs):
...      print args
...      print kwargs
...
>>> c = A()
>>> c(1,2,3,a=1,b=2,c=3)
(1, 2, 3)
{'a': 1, 'c': 3, 'b': 2}

 第七章 Python異常處理

什麼是異常?
顧名思義,異常就是程序由於某種緣由沒法正常工做了,好比縮進錯誤、缺乏軟件包、環境錯誤、鏈接超時等等都會引起異常。一個健壯的程序應該把所能預知的異常都應作相應的處理,應對一些簡單的異常狀況,使得更好的保證程序長時間運行。即便出了問題,也可以讓維護者一眼看出問題所在。所以本章節講解的就是怎麼處理異常,讓你的程序更加健壯。
7.1 捕捉異常語法
try...except...
try:
     expression
except [Except Type]:
     expression
7.2 異常類型
常見的異常類型:
異常類型
用途
SyntaxError
語法錯誤
IndentationError
縮進錯誤
TypeError  
對象類型與要求不符合
ImportError
模塊或包導入錯誤;通常路徑或名稱錯誤
KeyError
字典裏面不存在的鍵
NameError
變量不存在
IndexError
下標超出序列範圍
IOError
輸入/輸出異常;通常是沒法打開文件
AttributeError
對象裏沒有屬性
KeyboardInterrupt
鍵盤接受到Ctrl+C
Exception
通用的異常類型;通常會捕捉全部異常
還有一些異常類型,能夠經過dir查看:
>>> import exceptions
>>> dir(exceptions)
['ArithmeticError', 'AssertionError', 'AttributeError', 'BaseException', 'BufferError', 'BytesWarning', 'DeprecationWarning', 'EOFError', 'EnvironmentError', 'Exception', 'FloatingPointError', 'FutureWarning', 'GeneratorExit', 'IOError', 'ImportError', 'ImportWarning', 'IndentationError', 'IndexError', 'KeyError', 'KeyboardInterrupt', 'LookupError', 'MemoryError', 'NameError', 'NotImplementedError', 'OSError', 'OverflowError', 'PendingDeprecationWarning', 'ReferenceError', 'RuntimeError', 'RuntimeWarning', 'StandardError', 'StopIteration', 'SyntaxError', 'SyntaxWarning', 'SystemError', 'SystemExit', 'TabError', 'TypeError', 'UnboundLocalError', 'UnicodeDecodeError', 'UnicodeEncodeError', 'UnicodeError', 'UnicodeTranslateError', 'UnicodeWarning', 'UserWarning', 'ValueError', 'Warning', 'ZeroDivisionError', '__doc__', '__name__', '__package__']

博客地址:http://lizhenliang.blog.51cto.com and https://yq.aliyun.com/u/lizhenliang
QQ羣:323779636(Shell/Python運維開發羣)


7.3 異常處理
例如:打印一個沒有定義的變量
>>> print a
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'a' is not defined
會拋出異常,提示名字沒有定義。若是程序遇到這種狀況,就會終止。
那咱們能夠這樣,當沒有這個變量的時候就變量賦值,不然繼續操做。
>>> try:             
...   print a
... except NameError:
...   a = ""
...
>>> a
''
這樣就避免了異常的發生。在開發中每每不知道什麼是什麼異常類型,這時就能夠使用Exception類型來捕捉全部的異常:
例如:打印一個類對象裏面沒有的屬性
>>> class A:
...   a = 1
...   b = 2
...
>>> c = A()
>>> try:
...   print c.c
... except Exception:
...   print "Error..."
...
Error...
有時也想把異常信息也打印出來,怎麼作呢?
能夠把錯誤輸出保存到一個變量中,根據上面例子來:
>>> try:
...   print c.c
... except Exception, e:
...   print "Error: " + str(e)
...
Error: A instance has no attribute 'c'
 
# 也能夠使用as關鍵字將錯誤出輸出保存到變量中
>>> try:               
...   print c.c         
... except Exception as e:
...   print "Error: " + str(e)         
...
Error: A instance has no attribute 'c'
當出現的異常類型有幾種可能性時,能夠寫多個except:
>>> try:
...   print a
... except NameError, e:
...   print "NameError: " + str(e)
... except KeyError, e:
...   print "KeyError: " + str(e)
...
NameError: name 'a' is not defined
注意:except也能夠不指定異常類型,那麼會忽略全部的異常類,這樣作有風險的,它一樣會捕捉Ctrl+C、sys.exit等的操做。因此使用except Exception更好些。
7.4 else和finally語句
     7.4.1 else語句
   表示若是try中的代碼沒有引起異常,則會執行else。
   繼續按照上面定義的類舉例:
>>> try:
...   print c.a
... except Exception as e:
...   print e
... else:
...   print "else..."
...
1
else...
   7.4.2 finally語句
   表示不管是否異常,都會執行finally。
>>> try:
...   print c.c
... except Exception as e:
...   print e
... finally:
...   print "finally..."
...
A instance has no attribute 'c'
finally...
   通常用於清理工做,好比打開一個文件,無論是否文件是否操做成功,都應該關閉文件。
   7.4.3 try...except...else...finally
   這是一個完整的語句,當一塊兒使用時,使異常處理更加靈活。
#!/usr/bin/python
# -*- coding: utf-8 -*-
 
try:
    print a
except Exception as e:
    print "Error: " + str(e)
else:
    print "else..."
finally: 
    print "finally..."
 
# python test.py
python test.py
Error: name 'a' is not defined
finally...
     須要注意的是:它們語句的順序必須是try...except...else...finally,不然語法錯誤!裏面else和finally是可選的。
7.5 自定義異常類
raise語句用來手動拋出一個異常,使用方法:
raise ExceptType(ExceptInfo)
例如:拋出一個指定的異常
>>> raise NameError('test except...')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: test except...
raise參數必須是一個異常的實例或Exception子類。
上面用的Exception子類,那麼我定義一個異常的實例,須要繼承Exception類:
>>> class MyError(Exception):
...   def __init__(self, value):
...      self.value = value
...   def __str__(self):
...      return self.value
...
>>> raise MyError("MyError...")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
__main__.MyError: MyError...
7.6 assert語句
assert語句用於檢查條件表達式是否爲真,不爲真則觸發異常。又稱斷言語句。
通常用在某個條件爲真才能正常工做。
>>> assert 1==1
>>> assert 1!=1
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AssertionError
 
>>> assert range(4)==[0,1,2] 
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AssertionError
 
# 添加異常描述信息
>>> assert 1!=1, "assert description..."
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AssertionError: assert description...

 

第八章 Python可迭代對象、迭代器和生成器

  1. 雲棲社區>
  2. 博客>
  3. 正文

第八章 Python可迭代對象、迭代器和生成器

 
李振良 2016-10-22 10:40:11 瀏覽2276
8.1 可迭代對象(Iterable)
大部分對象都是可迭代,只要實現了__iter__方法的對象就是可迭代的。
__iter__方法會返回迭代器(iterator)自己,例如:
>>> lst = [1,2,3]
>>> lst.__iter__()
<listiterator object at 0x7f97c549aa50>
Python提供一些語句和關鍵字用於訪問可迭代對象的元素,好比for循環、列表解析、邏輯操做符等。
判斷一個對象是不是可迭代對象:
>>> from collections import Iterable  # 只導入Iterable方法
>>> isinstance('abc', Iterable)     
True
>>> isinstance(1, Iterable)     
False
>>> isinstance([], Iterable)
True
這裏的isinstance()函數用於判斷對象類型,後面會講到。
可迭代對象通常都用for循環遍歷元素,也就是能用for循環的對象均可稱爲可迭代對象。
例如,遍歷列表:
>>> lst = [1, 2, 3]
>>> for i in lst:
...   print i
...
1
2
3

博客地址:http://lizhenliang.blog.51cto.com and https://yq.aliyun.com/u/lizhenliang
QQ羣:323779636(Shell/Python運維開發羣)


8.2 迭代器(Iterator)
具備next方法的對象都是迭代器。在調用next方法時,迭代器會返回它的下一個值。若是next方法被調用,但迭代器沒有值能夠返回,就會引起一個StopIteration異常。
使用迭代器的好處:
1)若是使用列表,計算值時會一次獲取全部值,那麼就會佔用更多的內存。而迭代器則是一個接一個計算。
2)使代碼更通用、更簡單。
   8.2.1 迭代器規則
   回憶下在Python數據類型章節講解到字典迭代器方法,來舉例說明下迭代器規則:
>>> d = {'a':1, 'b':2, 'c':3}
>>> d.iteritems()
<dictionary-itemiterator object at 0x7f97c3b1bcb0>
 
# 判斷是不是迭代器
>>> from collections import Iterator
>>> isinstance(d, Iterator)
False
>>> isinstance(d.iteritems(), Iterator)
True
 
# 使用next方法。
>>> iter_items = d.iteritems()
>>> iter_items.next()
('a', 1)
>>> iter_items.next()
('c', 3)
>>> iter_items.next()
('b', 2)
因爲字典是無序的,因此顯示的是無序的,實際是按照順序獲取的下一個元素。
   8.2.2 iter()函數
   使用iter()函數轉換成迭代器:
語法:
  iter(collection) -> iterator
  iter(callable, sentinel) -> iterator
>>> lst = [1, 2, 3]
>>> isinstance(lst, Iterator)
False
>>> lst.next()  # 不是迭代器是不具有next()屬性的
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'list' object has no attribute 'next'
>>> iter_lst = iter(lst)             
>>> isinstance(iter_lst, Iterator)
True
 
>>> iter_lst.next()
1
>>> iter_lst.next()
2
>>> iter_lst.next()
3
   8.2.3 itertools模塊
itertools模塊是Python內建模塊,提供可操做迭代對象的函數。能夠生成迭代器,也能夠生成無限的序列迭代器。
有下面幾種生成無限序列的方法:
count([n]) --> n, n+1, n+2, ...
cycle(p) --> p0, p1, ... plast, p0, p1, ...
repeat(elem [,n]) --> elem, elem, elem, ... endlessly or up to n times 
也有幾個操做迭代器的方法:
   islice(seq, [start,] stop [, step]) --> elements from
chain(p, q, ...) --> p0, p1, ... plast, q0, q1, ...
groupby(iterable[, keyfunc]) --> sub-iterators grouped by value of keyfunc(v) 
imap(fun, p, q, ...) --> fun(p0, q0), fun(p1, q1), ...
ifilter(pred, seq) --> elements of seq where pred(elem) is True
 1)count生成序列迭代器
>>> from itertools import *  # 導入全部方法
      # 用法 count(start=0, step=1) --> count object
>>> counter = count()    
>>> counter.next()
0
>>> counter.next()
1
>>> counter.next()
2
...... 
能夠使用start參數設置開始值,step設置步長。
    2)cycle用可迭代對象生成迭代器
      # 用法 cycle(iterable) --> cycle object
>>> i = cycle(['a', 'b', 'c'])  
>>> i.next()
'a'
>>> i.next()
'b'
>>> i.next()
'c'
   3)repeat用對象生成迭代器
# 用法 repeat(object [,times]) -> create an iterator which returns the object,就是任意對象
>>> i = repeat(1)
>>> i.next()
1
>>> i.next()
1
>>> i.next()
1
......
可以使用無限次。
 
也能夠指定次數:
     >>> i = repeat(1, 2)
>>> i.next()
1
>>> i.next()
1
>>> i.next()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration
   4)islice用可迭代對象並設置結束位置
      # 用法 islice(iterable, [start,] stop [, step]) --> islice object
>>> i = islice([1,2,3],2)   
>>> i.next()             
1
>>> i.next()
2
>>> i.next()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration
正常的話也能夠獲取的3。
   5)chain用多個可迭代對象生成迭代器
# 用法 chain(*iterables) --> chain object
>>> i = chain('a','b','c')
>>> i.next()
'a'
>>> i.next()
'b'
>>> i.next()
'c'
   6)groupby將可迭代對象中重複的元素挑出來放到一個迭代器中
# 用法 groupby(iterable[, keyfunc]) -> create an iterator which returns
>>> for key,group in groupby('abcddCca'):
...   print key,list(group)               
...
a ['a']
b ['b']
c ['c']
d ['d', 'd']
C ['C']
c ['c']
a ['a']
groupby方法是區分大小寫的,若是想把大小寫的都放到一個迭代器中,能夠定義函數處理下:
>>> for key,group in groupby('abcddCca', lambda c: c.upper()):
...   print key, list(group)
...
A ['a']
B ['b']
C ['c']
D ['d', 'd']
C ['C', 'c']
A ['a']
   7)imap用函數處理多個可迭代對象
# 用法 imap(func, *iterables) --> imap object
>>> a = imap(lambda x, y: x * y,[1,2,3],[4,5,6])   
>>> a.next()
4
>>> a.next()
10
>>> a.next()
18
   8)ifilter過濾序列
# 用法 ifilter(function or None, sequence) --> ifilter object
>>> i = ifilter(lambda x: x%2==0,[1,2,3,4,5])
>>> for i in i:
...   print i
...
2
4
當使用for語句遍歷迭代器時,步驟大體這樣的,先調用迭代器對象的__iter__方法獲取迭代器對象,再調用對象的__next__()方法獲取下一個元素。最後引起StopIteration異常結束循環。
8.3 生成器(Generator)
什麼是生成器?
1)任何包含yield語句的函數都稱爲生成器。
2)生成器都是一個迭代器,但迭代器不必定是生成器。
8.3.1 生成器函數
在函數定義中使用yield語句就建立了一個生成器函數,而不是普通的函數。
當調用生成器函數時,每次執行到yield語句,生成器的狀態將被凍結起來,並將結果返回__next__調用者。凍結意思是局部的狀態都會被保存起來,包括局部變量綁定、指令指針。確保下一次調用時能從上一次的狀態繼續。
以生成斐波那契數列舉例說明yield使用:
斐波那契(Fibonacci)數列是一個簡單的遞歸數列,任意一個數均可以由前兩個數相加獲得。
#!/usr/bin/python
# -*- coding: utf-8 -*-
def fab(max):
    n, a, b = 0, 0, 1
    while n < max:
        print b
        a, b = b, a + b
        n += 1
fab(5)
 
# python test.py
1
1
2
3
5
使用yied語句,只須要把print b改爲yield b便可:
#!/usr/bin/python
# -*- coding: utf-8 -*-
def fab(max):
    n, a, b = 0, 0, 1
    while n < max:
        yield b
        # print b
        a, b = b, a + b
        n += 1
print fab(5)
 
# python test.py
<generator object fab at 0x7f2369495820>
可見,調用fab函數不會執行fab函數,而是直接返回了一個生成器對象,上面說過生成器就是一個迭代器。那麼就能夠經過next方法來返回它下一個值。
>>> import test
>>> f = test.fab(5)   
>>> f.next()       
1
>>> f.next()                               
1
>>> f.next()
2
>>> f.next()
3
>>> f.next()
5
每次fab函數的next方法,就會執行fab函數,執行到yield b時,fab函數返回一個值,下一次執行next方法時,代碼從yield b的嚇一跳語句繼續執行,直到再遇到yield。
8.3.2 生成器表達式
在第四章 Python運算符和流程控制章節講過,簡化for和if語句,使用小括號()返回一個生成器,中括號[]生成一個列表。
回顧下:
# 生成器表達式
>>> result = (x for x in range(5))
>>> result
<generator object <genexpr> at 0x030A4FD0>
>>> type(result)
<type 'generator'>
 
# 列表解析表達式
>>> result = [ x for x in range(5)]
>>> type(result)
<type 'list'>
>>> result
[0, 1, 2, 3, 4]
第一個就是生成器表達式,返回的是一個生成器,就能夠使用next方法,來獲取下一個元素:
>>> result.next()
0
>>> result.next()
1
>>> result.next()
2
......

 

第九章 Python自定義模塊及導入方法

 

9.1 自定義模塊
自定義模塊你已經會了,日常寫的代碼放到一個文件裏面就是啦!
例如,寫個簡單的函數,做爲一個模塊:
#!/usr/bin/python
# -*- coding: utf-8 -*-
 
def func(a, b):
   return a * b
class MyClass:
   def __init__(self, a, b):
        self.a = a
        self.b = b
   def method(self):
        return self.a * self.b
導入模塊:
>>> import test
>>> test.func(2, 2)
4
>>> c = test.MyClass(2, 2)
>>> c.method()
4
是否是很簡單!是的,沒錯,就是這樣。
須要注意的是,test就是文件名。另外,模塊名要能找到,個人是在當前目錄下。
有時常常from...import...,這又是啥呢,來看看:
>>> from test import func, MyClass  # 多個函數或類以逗號分隔
>>> test.func(2, 2)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'test' is not defined
>>> func(2, 2)
4
>>> c = MyClass(2, 2)
>>> c.method()
4
看到了吧!若是你不想把模塊裏的函數都導入,就能夠這樣。一方面避免導入過多用不到的函數增長負載,另外一方面引用時可不加模塊名。
若是想調用不加模塊名,也想導入全部模塊,能夠這樣:
>>> from test import *
>>> func(2, 2)
4
>>> c = MyClass(2, 2)
>>> c.method()
4
使用個星號就表明了全部。
提醒:在模塊之間引用也是一樣的方式。

博客地址:http://lizhenliang.blog.51cto.comnode

QQ羣:323779636(Shell/Python運維開發羣)python


9.2 做爲腳原本運行程序
全部的模塊都有一個內置屬性__name__,若是import一個模塊,那麼模塊的__name__屬性返回值通常是文件名。若是直接運行Python程序,__name__的值將是一個"__mian__"。
舉例說明,根據上面程序作一個測試:
#!/usr/bin/python
# -*- coding: utf-8 -*-
 
def func(a, b):
   return a * b
class MyClass:
   def __init__(self, a, b):
        self.a = a
        self.b = b
   def method(self):
        return self.a * self.b
print __name__
 
# python test.py
__main__
與預期同樣,打印出了「__main__」,再建立一個test2.py,導入這個模塊:
#!/usr/bin/python
# -*- coding: utf-8 -*-
import test
 
# python test2.py
test
打印出了模塊名,這個結果輸出就是test.py中的print __name__。
因此,咱們在test.py裏面判斷下__name__值等於__main__時說明在手動執行這個程序:
#!/usr/bin/python
# -*- coding: utf-8 -*-
 
def func(a, b):
   return a * b
class MyClass:
   def __init__(self, a, b):
        self.a = a
        self.b = b
   def method(self):
        return self.a * self.b
 
if __name__ == "__main__":
   print "我在手動執行這個程序..."
 
# python test.py
我在手動執行這個程序...
 
此時再運行test2.py試試,是否是打印爲空!明白了吧!
9.3 安裝第三方模塊
在Python中安裝外部的模塊有幾種方式:
1)下載壓縮包,經過setuptools工具安裝,這個在第一章Python基礎知識裏面用到過。推薦下載地址:http://pypi.python.org
2)easy_install工具安裝,也依賴setuptools。
3)pip工具安裝。推薦使用這個方式。
4)直接將壓縮包解壓到Python模塊目錄。但經常會出現import失敗,不推薦。
5)在Windows下,除了上面幾種方式之外,能夠直接下載exe文件點擊一步步安裝。
pip與easy_install安裝方式相似,主要區別在於easy_install不支持卸載軟件,而pip支持。
推薦使用pip命令安裝,簡單方便。若是安裝失敗能夠按順序這麼嘗試:方式1 --> 方式2 --> 方式4
以安裝setuptools舉例上面幾種安裝方式:
方式1:
# wget https://pypi.python.org/packages/32/3c/e853a68b703f347f5ed86585c2dd2828a83252e1216c1201fa6f81270578/setuptools-26.1.1.tar.gz
# tar zxvf setuptools-26.1.1.tar.gz
# cd setuptools-26.1.1
# python setup.py install
方式2:
# easy_install setuptools
方式3:
# pip install setuptools
# pip uninstall setuptools  # 卸載
# pip search setuptools  # 搜索
方式3:
cp -rf setuptools-26.1.1 /usr/local/lib/python2.7/dist-packages
9.4 查看模塊幫助文檔
前面幾個章節已經使用幾個內置模塊了,好比collections、itertools等,導入與上面同樣,這裏再也不過多說明了。
    1)help()函數
   當一個模塊對其語法不瞭解時,能夠查看幫助,以collections舉例:
>>> import collections
>>> help(collections)
Help on module collections:
 
NAME
    collections
 
FILE
    /usr/lib/python2.7/collections.py
 
MODULE DOCS
    http://docs.python.org/library/collections  # 注意:這裏是這個模塊的幫助文檔,很詳細的哦!
 
CLASSES
    __builtin__.dict(__builtin__.object)
        Counter
        OrderedDict
        defaultdict
    __builtin__.object
        _abcoll.Callable
        _abcoll.Container
......
   使用help()就能查看這個模塊的內部構造,包括類方法、屬性等信息。
   也能夠再對某個方法查看其用法:
>>> help(collections.Counter())
Help on Counter in module collections object:
 
class Counter(__builtin__.dict)
 |  Dict subclass for counting hashable items.  Sometimes called a bag
 |  or multiset.  Elements are stored as dictionary keys and their counts
 |  are stored as dictionary values.
 | 
 |  >>> c = Counter('abcdeabcdabcaba')  # count elements from a string
 | 
 |  >>> c.most_common(3)                # three most common elements
 |  [('a', 5), ('b', 4), ('c', 3)]
 |  >>> sorted(c)                       # list all unique elements
 |  ['a', 'b', 'c', 'd', 'e']
 |  >>> ''.join(sorted(c.elements()))   # list elements with repetitions
 |  'aaaaabbbbcccdde'
 |  >>> sum(c.values())                 # total of all counts
 |  15
 | 
 |  >>> c['a']                          # count of letter 'a'
 ......
   通常裏面都是舉例說明,可快速幫助咱們回憶使用方法。
    2)dir()函數查看對象屬性
    這個在前面也用到過,能看到對象的方法、屬性等信息:
>>> dir(collections)
['Callable', 'Container', 'Counter', 'Hashable', 'ItemsView', 'Iterable', 'Iterator', 'KeysView', 'Mapping', 'MappingView', 'MutableMapping', 'MutableSequence', 'MutableSet', 'OrderedDict', 'Sequence', 'Set', 'Sized', 'ValuesView', '__all__', '__builtins__', '__doc__', '__file__', '__name__', '__package__', '_abcoll', '_chain', '_class_template', '_eq', '_field_template', '_get_ident', '_heapq', '_imap', '_iskeyword', '_itemgetter', '_repeat', '_repr_template', '_starmap', '_sys', 'defaultdict', 'deque', 'namedtuple']
     3)github上查看模塊用法
     Python官方模塊下載地址http://pypi.python.org,全部的模塊在這裏都有。
     打開網站後,在搜索框搜索你的模塊名,在結果找到模塊名點進去,會有一個 Home Page的鏈接,Python大多數模塊都是託管在github上面,這個連接就是這個模塊在github上面的地址,點擊後跳轉到github對應的模塊頁面,裏面也有很詳細模塊使用方法。
9.5 導入模塊新手容易出現的問題
還有一個新手常常犯的問題,寫一個模塊,好比使用itertools模塊,爲了說明這個測試文件是這個模塊,就把文件名寫成了這個模塊名,因而就形成了下面錯誤:
#!/usr/bin/python
# -*- coding: utf-8 -*-
import collections
c = collections.Counter()
for i in "Hello world!":
   c[i] += 1
print c
 
# python collections.py
Traceback (most recent call last):
  File "collections.py", line 3, in <module>
    import collections
  File "/home/user/collections.py", line 4, in <module>
    c = collections.Counter()
AttributeError: 'module' object has no attribute 'Counter'
拋出異常,明明在解釋器裏面能夠正常導入使用啊,怎麼會提示沒Counter屬性呢,問題就出現你的文件名與導入的模塊名重名,致使程序import了這個文件,上面講過文件名就是模塊名。因此文件名不要與引用的模塊名相同。
還有一個使用方法也說明下,使用as關鍵字設置模塊別名,這樣使用中就不用輸入那麼長的模塊名了,按照上面的例子,把名字先改爲collections1.py,作測試:
#!/usr/bin/python
# -*- coding: utf-8 -*-
import collections as cc
c = cc.Counter()
for i in "Hello world!":
   c[i] += 1
print c
 
# python collections1.py
Counter({'l': 3, 'o': 2, '!': 1, ' ': 1, 'e': 1, 'd': 1, 'H': 1, 'r': 1, 'w': 1})

 

第十章 Python經常使用標準庫/模塊使用(必會)

本章涉及標準庫:mysql

一、sys
linux

二、osnginx

三、globgit

四、mathgithub

五、randomweb

六、platform

七、pikle與cPikle

八、subprocess

九、Queue

十、StringIO

十一、logging

十二、ConfigParser

1三、urllib與urllib2

1四、json

1五、time

1六、datetime
 
10.1 sys
1)sys.argv
命令行參數。
argv[0] #表明自己名字
argv[1] #第一個參數
argv[2] #第二個參數
argv[3] #第三個參數
argv[N] #第N個參數
argv #參數以空格分隔存儲到列表。
看看使用方法:

#!/usr/bin/python
# -*- coding: utf-8 -*-
import sys
print sys.argv[0]
print sys.argv[1]
print sys.argv[2]
print sys.argv[3]
print sys.argv
print len(sys.argv)

# python test.py
test.py
a
b
c
c
['test.py', 'a', 'b', 'c']
4
值得注意的是,argv既然是一個列表,那麼能夠經過len()函數獲取這個列表的長度從而知道輸入的參數數量。能夠看到列表把自身文件名也寫了進去,因此當咱們統計的使用應該-1纔是實際的參數數量,所以能夠len(sys.argv[1:])獲取參數長度。
2)sys.path
模塊搜索路徑。

>>> sys.path
['', '/usr/local/lib/python2.7/dist-packages/tornado-3.1-py2.7.egg', '/usr/lib/python2.7', '/usr/lib/python2.7/plat-x86_64-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']
輸出的是一個列表,裏面包含了當前Python解釋器所能找到的模塊目錄。
若是想指定本身的模塊目錄,能夠直接追加:

>>> sys.path.append('/opt/scripts')
>>> sys.path
['', '/usr/local/lib/python2.7/dist-packages/tornado-3.1-py2.7.egg', '/usr/lib/python2.7', '/usr/lib/python2.7/plat-x86_64-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', '/opt/scripts']
3)sys.platform
系統平臺標識符。
系統 平臺標識符
Linux linux
Windows win32
Windows/Cygwin cygwin
Mac OS X
darwin
 
>>> sys.platform
'linux2'
Python自己就是跨平臺語言,但也不就意味着全部的模塊都是在各類平臺通用,因此能夠使用這個方法判斷當前平臺,作相應的操做。
4)sys.subversion
在第一章講過Python解釋器有幾種版本實現,而默認解釋器是CPython,來看看是否是:
>>> sys.subversion
('CPython', '', '')
5)sys.version
查看Python版本:
>>> sys.version
'2.7.6 (default, Jun 22 2015, 17:58:13) \n[GCC 4.8.2]'
6)sys.exit()
退出解釋器:
#!/usr/bin/python
# -*- coding: utf-8 -*-
import sys
print "Hello world!"
sys.exit()
print "Hello world!"

# python test.py
Hello world!
代碼執行到sys.exit()就會終止程序。
7)sys.stdin、sys.stdout和sys.stderr
標準輸入、標準輸出和錯誤輸出。
標準輸入:通常是鍵盤。stdin對象爲解釋器提供輸入字符流,通常使用raw_input()和input()函數。
例如:讓用戶輸入信息
#!/usr/bin/python
# -*- coding: utf-8 -*-
import sys
name = raw_input("Please input your name: ")
print name

# python test.py
Please input your name: xiaoming
xiaoming
 
import sys
print "Please enter your name: "
name = sys.stdin.readline()
print name
 
# python b.py
Please enter your name:
xiaoming
xiaoming
再例如,a.py文件標準輸出做爲b.py文件標準輸入:
# cat a.py
import sys
sys.stdout.write("123456\n")
sys.stdout.flush()
# cat b.py
import sys
print sys.stdin.readlines()
 
# python a.py | python b.py
['123456\n']
sys.stdout.write()方法其實就是下面所講的標準輸出,print語句就是調用了這個方法。
標準輸出:通常是屏幕。stdout對象接收到print語句產生的輸出。
例如:打印一個字符串
#!/usr/bin/python
# -*- coding: utf-8 -*-
import sys
print "Hello world!"

# python test.py
Hello world!
sys.stdout是有緩衝區的,好比:
import sys
import time
for i in range(5):
    print i,
    # sys.stdout.flush()
    time.sleep(1)
# python test.py
0 1 2 3 4
本是每隔一秒輸出一個數字,但如今是循環完纔會打印全部結果。若是把sys.stdout.flush()去掉,就會沒執行到print就會刷新stdout輸出,這對實時輸出信息的程序有幫助。
錯誤輸出:通常是錯誤信息。stderr對象接收出錯的信息。
例如:引起一個異常
>>> raise Exception, "raise..."
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
Exception: raise...

博客地址:http://lizhenliang.blog.51cto.com and https://yq.aliyun.com/u/lizhenliang
QQ羣:323779636(Shell/Python運維開發羣)

10.2 os
os模塊主要對目錄或文件操做。
方法 描述 示例
os.name 返回操做系統類型 返回值是"posix"表明linux,"nt"表明windows
os.extsep 返回一個"."標識符  
os.environ 以字典形式返回系統變量  
os.devnull 返回/dev/null標識符  
os.linesep 返回一個換行符"\n" >>> print "a" + os.linesep + "b"
a
b
os.sep 返回一個路徑分隔符正斜槓"/" >>> "a" + os.sep + "b"
'a/b'
os.listdir(path) 列表形式列出目錄  
os.getcwd() 獲取當前路徑 >>> os.getcwd()
'/home/user'
os.chdir(path) 改變當前工做目錄到指定目錄 >>> os.chdir('/opt')
>>> os.getcwd()
'/opt'
os.mkdir(path [, mode=0777]) 建立目錄 >>> os.mkdir('/home/user/test')
os.makedirs(path [, mode=0777]) 遞歸建立目錄 >>> os.makedirs('/home/user/abc/abc')
os.rmdir(path) 移除空目錄 >>> os.makedirs('/home/user/abc/abc')
os.remove(path) 移除文件  
os.rename(old, new) 重命名文件或目錄  
os.stat(path) 獲取文件或目錄屬性  
os.chown(path, uid, gid) 改變文件或目錄全部者  
os.chmod(path, mode) 改變文件訪問權限 >>> os.chmod('/home/user/c/a.tar.gz', 0777) 
os.symlink(src, dst) 建立軟連接  
os.unlink(path) 移除軟連接 >>> os.unlink('/home/user/ddd')
urandom(n) 返回隨機字節,適合加密使用 >>> os.urandom(2)
'%\xec'
os.getuid() 返回當前進程UID  
os.getlogin() 返回登陸用戶名  
os.getpid() 返回當前進程ID  
os.kill(pid, sig) 發送一個信號給進程  
os.walk(path) 目錄樹生成器,返回格式:(dirpath, [dirnames], [filenames]) >>> for root, dir, file in os.walk('/home/user/abc'):
...   print root
...   print dir
...   print file
os.statvfs(path)    
os.system(command) 執行shell命令,不能存儲結果  
popen(command [, mode='r' [, bufsize]]) 打開管道來自shell命令,並返回一個文件對象 >>> result = os.popen('ls')
>>> result.read()
os.path類用於獲取文件屬性。
os.path.basename(path) 返回最後一個文件或目錄名 >>> os.path.basename('/home/user/a.sh')
'a.sh'
os.path.dirname(path) 返回最後一個文件前面目錄 >>> os.path.dirname('/home/user/a.sh')
'/home/user'
os.path.abspath(path) 返回一個絕對路徑 >>> os.path.abspath('a.sh')
'/home/user/a.sh'
os.path.exists(path) 判斷路徑是否存在,返回布爾值 >>> os.path.exists('/home/user/abc')
True
os.path.isdir(path) 判斷是不是目錄  
os.path.isfile(path) 判斷是不是文件  
os.path.islink(path) 判斷是不是連接  
os.path.ismount(path) 判斷是否掛載  
os.path.getatime(filename) 返回文件訪問時間戳 >>> os.path.getctime('a.sh')
1475240301.9892483
os.path.getctime(filename) 返回文件變化時間戳  
os.path.getmtime(filename) 返回文件修改時間戳  
os.path.getsize(filename) 返回文件大小,單位字節  
os.path.join(a, *p) 加入兩個或兩個以上路徑,以正斜槓"/"分隔。經常使用於拼接路徑 >>> os.path.join('/home/user','test.py','a.py')
'/home/user/test.py/a.py'
os.path.split( 分隔路徑名 >>> os.path.split('/home/user/test.py')
('/home/user', 'test.py')
os.path.splitext( 分隔擴展名 >>> os.path.splitext('/home/user/test.py')
('/home/user/test', '.py')
10.3 glob
文件查找,支持通配符(*、?、[])
# 查找目錄中全部以.sh爲後綴的文件
>>> glob.glob('/home/user/*.sh')
['/home/user/1.sh', '/home/user/b.sh', '/home/user/a.sh', '/home/user/sum.sh']
# 查找目錄中出現單個字符並以.sh爲後綴的文件
>>> glob.glob('/home/user/?.sh')
['/home/user/1.sh', '/home/user/b.sh', '/home/user/a.sh']
# 查找目錄中出現a.sh或b.sh的文件
>>> glob.glob('/home/user/[a|b].sh')
['/home/user/b.sh', '/home/user/a.sh']
10.4 math
數字處理。
下面列出一些本身決定會用到的:
方法 描述 示例
math.pi 返回圓周率 >>> math.pi
3.141592653589793
math.ceil(x) 返回x浮動的上限 >>> math.ceil(5.2)
6.0
math.floor(x) 返回x浮動的下限 >>> math.floor(5.2)
5.0
math.trunc(x) 將數字截尾取整 >>> math.trunc(5.2)
5
math.fabs(x) 返回x的絕對值 >>> math.fabs(-5.2)
5.2
math.fmod(x,y) 返回x%y(取餘) >>> math.fmod(5,2) 
1.0
math.modf(x) 返回x小數和整數 >>> math.modf(5.2)
(0.20000000000000018, 5.0)
math.factorial(x) 返回x的階乘 >>> math.factorial(5)
120
math.pow(x,y) 返回x的y次方 >>> math.pow(2,3)
8.0
math.sprt(x) 返回x的平方根 >>> math.sqrt(5)
2.2360679774997898
10.5 random
生成隨機數。
經常使用的方法:
方法 描述 示例
random.randint(a,b) 返回整數a和b範圍內數字 >>> random.randint(1,10)
6
random.random() 返回隨機數,它在0和1範圍內 >>> random.random()
0.7373251914304791
random.randrange(start, stop[, step]) 返回整數範圍的隨機數,並能夠設置只返回跳數 >>> random.randrange(1,10,2)
5
random.sample(array, x) 從數組中返回隨機x個元素 >>> random.sample([1,2,3,4,5],2)
[2, 4]
10.6 platform
獲取操做系統詳細信息。
方法 描述 示例
platform.platform() 返回操做系統平臺 >>> platform.platform()
'Linux-3.13.0-32-generic-x86_64-with-Ubuntu-14.04-trusty'
platform.uname() 返回操做系統信息 >>> platform.uname()
('Linux', 'ubuntu', '3.13.0-32-generic', '#57-Ubuntu SMP Tue Jul 15 03:51:08 UTC 2014', 'x86_64', 'x86_64')
platform.system() 返回操做系統平臺 >>> platform.system()
'Linux'
platform.version() 返回操做系統版本 >>> platform.version()
'#57-Ubuntu SMP Tue Jul 15 03:51:08 UTC 2014'
platform.machine() 返回計算機類型 >>> platform.machine()
'x86_64'
platform.processor() 返回計算機處理器類型 >>> platform.processor()
'x86_64'
platform.node() 返回計算機網絡名 >>> platform.node()   
'ubuntu'
platform.python_version() 返回Python版本號 >>> platform.python_version()
'2.7.6'
10.7 pickle與cPickle
建立可移植的Python序列化對象,持久化存儲到文件。
1)pickle
pickle庫有兩個經常使用的方法, dump()、load() 和 dumps()、 loads() ,下面看看它們的使用方法:
dump()方法是把對象保存到文件中。
格式:dump(obj, file, protocol=None)
load()方法是從文件中讀數據,重構爲原來的Python對象。
格式:load(file)
示例,將字典序列化到文件:
>>> import pickle
>>> dict = {'a':1, 'b':2, 'c':3}
>>> output = open('data.pkl', 'wb')  # 二進制模式打開文件
>>> pickle.dump(dict, output)   # 執行完導入操做,當前目錄會生成data.pkl文件
>>> output.close()  # 寫入數據並關閉
看看pickle格式後的文件:
# cat data.pkl
(dp0
S'a'
p1
I1
sS'c'
p2
I3
sS'b'
p3
I2
s.
讀取序列化文件:
>>> f = open('data.pkl')
>>> data = pickle.load(f)
>>> print data
{'a': 1, 'c': 3, 'b': 2}
用法挺簡單的,就是先導入文件,再讀取文件。
接下來看看序列化字符串操做:
dumps()返回一個pickle格式化的字符串
格式:dumps(obj, protocol=None)
load()解析pickle字符串爲對象
示例:
>>> s = 'abc'
>>> pickle.dumps(s) 
"S'abc'\np0\n."
>>> pkl = pickle.dumps(s)
>>> pkl
"S'abc'\np0\n."
>>> pickle.loads(pkl)
'abc'
須要注意的是,py2.x使用的是pickle2.0格式版本,若是用3.0、4.0版本的pickle導入會出錯。能夠經過pickle.format_version 查看版本。
2)cPickle
cPickle庫是C語言實現,對pickle進行了優化,提高了性能,建議在寫代碼中使用。
cPicke提供了與pickle相同的dump()、load() 和dumps()、 loads()方法,用法同樣,再也不講解。
10.8 subprocess
subprocess庫會fork一個子進程去執行任務,鏈接到子進程的標準輸入、輸出、錯誤,並得到它們的返回代碼。這個模塊將取代os.system、os.spawn*、os.popen*、popen2.*和commands.*。
提供瞭如下經常使用方法幫助咱們執行bash命令的相關操做:
subprocess.call():運行命令與參數。等待命令完成,返回執行狀態碼。
>>> import subprocess 
>>> retcode = subprocess.call(["ls", "-l"])
total 504
-rw-r--r-- 1 root root      54 Nov  2 06:15 data.pkl
>>> retcode
0
>>> retcode = subprocess.call(["ls", "a"]) 
ls: cannot access a: No such file or directory
>>> retcode
2
 
# 也能夠這樣寫
>>> subprocess.call('ls -l', shell=True)
subprocess.check_call():運行命令與參數。若是退出狀態碼非0,引起CalledProcessError異常,包含狀態碼。
>>> subprocess.check_call("ls a", shell=True)
ls: cannot access a: No such file or directory
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python2.7/subprocess.py", line 540, in check_call
    raise CalledProcessError(retcode, cmd)
subprocess.CalledProcessError: Command 'ls a' returned non-zero exit status 2
subprocess.Popen():這個類咱們主要來使用的,參數較多。
參數
描述
args
命令,字符串或列表
bufsize
0表明無緩衝,1表明行緩衝,其餘正值表明緩衝區大小,負值採用默認系統緩衝(通常是全緩衝)
executable
 
stdin
stdout
stderr
默認沒有任何重定向,能夠指定重定向到管道(PIPE)、文件對象、文件描述符(整數),stderr還能夠設置爲STDOUT
preexec_fn
鉤子函數,在fork和exec之間執行
close_fds
 
shell
爲True,表示用當前默認解釋器執行。至關於args前面添加「/bin/sh」「-c或win下"cmd.exe /c "
cwd
指定工做目錄
env
設置環境變量
universal_newlines
換行符統一處理成"\n"
startupinfo
在windows下的Win32 API 發送CreateProcess()建立進程
creationflags
在windows下的Win32 API 發送CREATE_NEW_CONSOLE()建立控制檯窗口
subprocess.Popen()類又提供瞭如下些方法:
方法
描述
Popen.communicate(input=None)
與子進程交互。讀取從stdout和stderr緩衝區內容,阻塞父進程,等待子進程結束
Popen. kill()
殺死子進程,在Posix系統上發送SIGKILL信號
Popen.pid
獲取子進程PID
Popen.poll()
若是子進程終止返回狀態碼
Popen.returncode
返回子進程狀態碼
Popen.send_signal(signal)
發送信號到子進程
Popen.stderr
若是參數值是PIPE,那麼這個屬性是一個文件對象,提供子進程錯誤輸出。不然爲None
Popen.stdin
若是參數值是PIPE,那麼這個屬性是一個文件對象,提供子進程輸入。不然爲None
Popen.stdout
若是參數值是PIPE,那麼這個屬性是一個文件對象,提供子進程輸出。不然爲None
Popen.terminate()
終止子進程,在Posix系統上發送SIGTERM信號,在windows下的Win32 API發送TerminateProcess()到子進程
Popen.wait()
等待子進程終止,返回狀態碼
示例:
>>> p = subprocess.Popen('dmesg |grep eth0', stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
>>> p.communicate() 
......  # 元組形式返回結果
 
>>> p.pid
57039
>>> p.wait()
0
>>> p.returncode
0
subprocess.PIPE提供了一個緩衝區,將stdout、stderr放到這個緩衝區中,p.communicate()方法讀取緩衝區數據。
緩衝區的stdout、stderr是分開的,能夠以p.stdout.read()方式得到標準輸出、錯誤輸出的內容。
再舉個例子,咱們以標準輸出做爲下個Popen任務的標準輸入:
>>> p1 = subprocess.Popen('ls', stdout=subprocess.PIPE, shell=True)
>>> p2 = subprocess.Popen('grep data', stdin=p1.stdout, stdout=subprocess.PIPE, shell=True)
>>> p1.stdout.close()   # 調用後啓動p2,爲了得到SIGPIPE
>>> output = p2.communicate()[0]
>>> output
'data.pkl\n'
p1的標準輸出做爲p2的標準輸入。這個p2的stdin、stdout也能夠是個可讀、可寫的文件。
10 .9 Queue
隊列,數據存放在內存中,通常用於交換數據。
描述
Queue.Empty
當非阻塞get()或get_nowait()對象隊列上爲空引起異常
Queue.Full
當非阻塞put()或put_nowait()對象隊列是一個滿的隊列引起異常
Queue.LifoQueue(maxsize=0)  
構造函數爲後進先出隊列。maxsize設置隊列最大上限項目數量。小於或等於0表明無限。
Queue.PriorityQueue(maxsize=0)
構造函數爲一個優先隊列。級別越高越先出。
Queue.Queue(maxsize=0)
構造函數爲一個FIFO(先進先出)隊列。maxsize設置隊列最大上限項目數量。小於或等於0表明無限。
Queue.deque
雙端隊列。實現快速append()和popleft(),無需鎖。
Queue.heapq
堆排序隊列。
用到比較多的是Queue.Queue類,在這裏主要了解下這個。
它提供了一些操做隊列的方法:
方法
描述
Queue.empty()
若是隊列爲空返回True,不然返回False
Queue.full()
若是隊列是滿的返回True,不然返回False
Queue.get(block=True, timeout=None)
從隊列中刪除並返回一個項目。沒有指定項目,由於是FIFO隊列,若是隊列爲空會一直阻塞。timeout超時時間
Queue.get_nowait()  
從隊列中刪除並返回一個項目,不阻塞。會拋出異常。
Queue.join()
等待隊列爲空,再執行別的操做
Queue.put(item, block=True, timeout=None)
寫入項目到隊列
Queue.put_nowait()
寫入項目到隊列,不阻塞。與get同理
Queue.qsize()  
返回隊列大小
Queue.task_done()
表示原隊列的任務完成
示例:
>>> from Queue import Queue
>>> q = Queue.Queue()
>>> q.put('test')
>>> q.qsize()
1
>>> q.get()
'test'
>>> q.qsize()
0
>>> q.full()
False
>>> q.empty()
True
10.10 StringIO
StringIO庫將字符串存儲在內存中,像操做文件同樣操做。主要提供了一個StringIO類。
方法
描述
StringIO.close()
關閉
StringIO.flush()
刷新緩衝區
StringIO.getvalue()
獲取寫入的數據
StringIO.isatty()
 
StringIO.next()
讀取下一行,沒有數據拋出異常
StringIO.read(n=-1) 
默認讀取全部內容。n指定讀取多少字節
StringIO.readline(length=None)
默認讀取下一行。length指定讀取多少個字符
StringIO.readlines(sizehint=0)
默認讀取全部內容,以列表返回。sizehint指定讀取多少字節
StringIO.seek(pos, mode=0)
在文件中移動文件指針,從mode(0表明文件起始位置,默認。1表明當前位置。2表明文件末尾)偏移pos個字節
StringIO.tell()
返回當前在文件中的位置
StringIO.truncate()
截斷文件大小
StringIO.write(str)
寫字符串到文件
StringIO.writelines(iterable)
寫入序列,必須是一個可迭代對象,通常是一個字符串列表
能夠看到,StringIO方法與文件對象方法大部分都同樣,從而也就能方面的操做內存對象。
示例:
>>> f = StringIO()
>>> f.write('hello')
>>> f.getvalue()
'hello'
像操做文件對象同樣寫入。
用一個字符串初始化StringIO,能夠像讀文件同樣讀取:
>>> f = StringIO('hello\nworld!')
>>> f.read()
'hello\nworld!'
>>> s = StringIO('hello world!')
>>> s.seek(5)            # 指針移動到第五個字符,開始寫入      
>>> s.write('-')               
>>> s.getvalue()
'hello-world!'
10 .11 logging
記錄日誌庫。
有幾個主要的類:
logging.Logger
應用程序記錄日誌的接口
logging.Filter
過濾哪條日誌不記錄
logging.FileHandler
日誌寫到磁盤文件
logging.Formatter
定義最終日誌格式
日誌級別:
級別
數字值
描述
critical
50
危險
error
40
錯誤
warning
30
警告
info
20
普通訊息
debug
10
調試
noset
0
不設置
Formatter類能夠自定義日誌格式,默認時間格式weight%Y-%m-%d %H:%M:%S,有如下這些屬性:
%(name)s
日誌的名稱
%(levelno)s
數字日誌級別
%(levelname)s
文本日誌級別
%(pathname)s
調用logging的完整路徑(若是可用)
%(filename)s
文件名的路徑名
%(module)s
模塊名
%(lineno)d
調用logging的源行號
%(funcName)s
函數名
%(created)f
建立時間,返回time.time()值
%(asctime)s
字符串表示建立時間
%(msecs)d
毫秒錶示建立時間
%(relativeCreated)d
毫秒爲單位表示建立時間,相對於logging模塊被加載,一般應用程序啓動。
%(thread)d
線程ID(若是可用)
%(threadName)s
線程名字(若是可用)
%(process)d
進程ID(若是可用)
%(message)s
輸出的消息
示例:
#!/usr/bin/python
# -*- coding: utf-8 -*-
#--------------------------------------------------
# 日誌格式
#--------------------------------------------------
# %(asctime)s       年-月-日 時-分-秒,毫秒 2013-04-26 20:10:43,745
# %(filename)s      文件名,不含目錄
# %(pathname)s      目錄名,完整路徑
# %(funcName)s      函數名
# %(levelname)s     級別名
# %(lineno)d        行號
# %(module)s        模塊名
# %(message)s       消息體
# %(name)s          日誌模塊名
# %(process)d       進程id
# %(processName)s   進程名
# %(thread)d        線程id
# %(threadName)s    線程名
 
import logging
format = logging.Formatter('%(asctime)s - %(levelname)s %(filename)s [line:%(lineno)d] %(message)s')
 
# 建立日誌記錄器
info_logger = logging.getLogger('info')
# 設置日誌級別,小於INFO的日誌忽略
info_logger.setLevel(logging.INFO)
# 日誌記錄到磁盤文件
info_file = logging.FileHandler("info.log")
# info_file.setLevel(logging.INFO)
# 設置日誌格式
info_file.setFormatter(format)
info_logger.addHandler(info_file)
 
error_logger = logging.getLogger('error')
error_logger.setLevel(logging.ERROR)
error_file = logging.FileHandler("error.log")
error_file.setFormatter(format)
error_logger.addHandler(error_file)
 
# 輸出控制檯(stdout)
console = logging.StreamHandler()
console.setLevel(logging.DEBUG)
console.setFormatter(format)
info_logger.addHandler(console)
error_logger.addHandler(console)
 
if __name__ == "__main__":
    # 寫日誌
    info_logger.warning("info message.")
    error_logger.error("error message!")
# python test.py
2016-07-02 06:52:25,624 - WARNING test.py [line:49] info message.
2016-07-02 06:52:25,631 - ERROR test.py [line:50] error message!
# cat info.log
2016-07-02 06:52:25,624 - WARNING test.py [line:49] info message.
# cat error.log
2016-07-02 06:52:25,631 - ERROR test.py [line:50] error message!
上面代碼實現了簡單記錄日誌功能。分別定義了info和error日誌,將等於或高於日誌級別的日誌寫到日誌文件中。在小項目開發中把它單獨寫一個模塊,很方面在其餘代碼中調用。
須要注意的是,在定義多個日誌文件時,getLogger(name=None)類的name參數須要指定一個惟一的名字,若是沒有指定,日誌會返回到根記錄器,也就是意味着他們日誌都會記錄到一塊兒。

博客地址:http://lizhenliang.blog.51cto.com and https://yq.aliyun.com/u/lizhenliang
QQ羣:323779636(Shell/Python運維開發羣)


10
.12 ConfigParser
配置文件解析。
這個庫咱們主要用到ConfigParser.ConfigParser()類,對ini格式文件增刪改查。
ini文件固定結構:有多個部分塊組成,每一個部分有一個[標識],並有多個key,每一個key對應每一個值,以等號"="分隔。值的類型有三種:字符串、整數和布爾值。其中字符串能夠不用雙引號,布爾值爲真用1表示,布爾值爲假用0表示。註釋以分號";"開頭。
方法
描述
ConfigParser.add_section(section)
建立一個新的部分配置
ConfigParser.get(section, option, raw=False, vars=None)
獲取部分中的選項值,返回字符串
ConfigParser.getboolean(section, option)
獲取部分中的選項值,返回布爾值
ConfigParser.getfloat(section, option)
獲取部分中的選項值,返回浮點數
ConfigParser.getint(section, option)
獲取部分中的選項值,返回整數
ConfigParser.has_option(section, option)
檢查部分中是否存在這個選項
ConfigParser.has_section(section)
檢查部分是否在配置文件中
ConfigParser.items(section, raw=False, vars=None)
列表元組形式返回部分中的每個選項
ConfigParser.options(section)
列表形式返回指定部分選項名稱
ConfigParser.read(filenames)
讀取ini格式的文件
ConfigParser.remove_option( section, option)
移除部分中的選項
ConfigParser.remove_section(section, option)
移除部分
ConfigParser.sections()
列表形式返回全部部分名稱
ConfigParser.set(section, option, value)
設置選項值,存在則更新,不然添加
ConfigParser.write(fp)
寫一個ini格式的配置文件
舉例說明,寫一個ini格式文件,對其操做:
# cat config.ini
[host1]   
host = 192.168.1.1
port = 22
user = zhangsan
pass = 123
[host2]
host = 192.168.1.2
port = 22
user = lisi
pass = 456
[host3]
host = 192.168.1.3
port = 22
user = wangwu
pass = 789
1)獲取部分中的鍵值
#!/usr/bin/python
# -*- coding: utf-8 -*-
from ConfigParser import ConfigParser
conf = ConfigParser()
conf.read("config.ini")
section = conf.sections()[0]  # 獲取隨機的第一個部分標識
options = conf.options(section) # 獲取部分中的全部鍵
key = options[2]
value = conf.get(section, options[2]) # 獲取部分中鍵的值
print key, value
print type(value)
 
# python test.py
port 22
<type 'str'>
這裏有意打出來了值的類型,來講明下get()方法獲取的值都是字符串,若是有須要,能夠getint()獲取整數。測試發現,ConfigParser是從下向上讀取的文件內容!
2)遍歷文件中的每一個部分的每一個字段
#!/usr/bin/python
# -*- coding: utf-8 -*-
from ConfigParser import ConfigParser
conf = ConfigParser()
conf.read("config.ini")
sections = conf.sections()  # 獲取部分名稱 ['host3', 'host2', 'host1']
for section in sections:
    options = conf.options(section) # 獲取部分名稱中的鍵 ['user', 'host', 'port', 'pass']
    for option in options:
         value = conf.get(section, option) # 獲取部分中的鍵值
         print option + ": " + value  
    print "-------------"
 
# python test.py
user: wangwu
host: 192.168.1.3
port: 22
pass: 789
-------------
user: lisi
host: 192.168.1.2
port: 22
pass: 456
-------------
user: zhangsan
host: 192.168.1.1
port: 22
pass: 123
-------------
經過上面的例子,熟悉了sections()、options()和get(),能任意獲取文件的內容了。
也能夠使用items()獲取部分中的每一個選項:
from ConfigParser import ConfigParser
conf = ConfigParser()
conf.read("config.ini")
print conf.items('host1')
 
# python test.py
[('user', 'zhangsan'), ('host', '192.168.1.1'), ('port', '22'), ('pass', '123')]
3)更新或添加選項
from ConfigParser import ConfigParser
conf = ConfigParser()
conf.read("config.ini")
fp = open("config.ini", "w")   # 寫模式打開文件,供後面提交寫的內容
conf.set("host1", "port", "2222")  # 有這個選項就更新,不然添加
conf.write(fp)  # 寫入的操做必須執行這個方法
4)添加一部分,並添加選項
from ConfigParser import ConfigParser
conf = ConfigParser()
conf.read("config.ini")
fp = open("config.ini", "w")
conf.add_section("host4")   # 添加[host4]
conf.set("host4", "host", "192.168.1.4")
conf.set("host4", "port", "22")
conf.set("host4", "user", "zhaoliu")
conf.set("host4", "pass", "123")
conf.write(fp)
5)刪除一部分
from ConfigParser import ConfigParser
conf = ConfigParser()
conf.read("config.ini")
fp = open("config.ini", "w")
conf.remove_section('host4')  # 刪除[host4]
conf.remove_option('host3', 'pass')  # 刪除[host3]的pass選項
conf.write(fp)
10 .13 urllib與urllib2
打開URL。urllib2是urllib的加強版,新增了一些功能,好比Request()用來修改Header信息。可是urllib2還去掉了一些好用的方法,好比urlencode()編碼序列中的兩個元素(元組或字典)爲URL查詢字符串。
通常狀況下這兩個庫結合着用,那咱們也結合着瞭解下。
 
描述
urllib.urlopen(url, data=None, proxies=None)
讀取指定URL,建立類文件對象。data是隨着URL提交的數據(POST)
urllib/urllib2.quote(s, safe='/')
將字符串中的特殊符號轉十六進制表示。如:
quote('abc def') -> 'abc%20def'
urllib/urllib2.unquote(s)
與quote相反
urllib.urlencode(query, doseq=0)
將序列中的兩個元素(元組或字典)轉換爲URL查詢字符串
urllib.urlretrieve(url, filename=None, reporthook=None, data=None)
將返回結果保存到文件,filename是文件名
urllib2.Request(url, data=None, headers={}, origin_req_host=None, unverifiable=False)
通常訪問URL用urllib.urlopen(),若是要修改header信息就會用到這個。
data是隨着URL提交的數據,將會把HTTP請求GET改成POST。headers是一個字典,包含提交頭的鍵值對應內容。
urllib2.urlopen(url, data=None, timeout=<object object>)
timeout 超時時間,單位秒
urllib2.build_opener(*handlers)
構造opener
urllib2.install_opener(opener)
把新構造的opener安裝到默認的opener中,之後urlopen()會自動調用
urllib2.HTTPCookieProcessor(cookiejar=None)
Cookie處理器
urllib2.HTTPBasicAuthHandler
認證處理器
urllib2.ProxyHandler
代理處理器
urllib.urlopen()有幾個經常使用的方法:
方法
描述
getcode()
獲取HTTP狀態碼
geturl()
返回真實URL。有可能URL3xx跳轉,那麼這個將得到跳轉後的URL
info()
返回服務器返回的header信息。能夠經過它的方法獲取相關值
next()
獲取下一行,沒有數據拋出異常
read(size=-1)
默認讀取全部內容。size正整數指定讀取多少字節
readline(size=-1)
默認讀取下一行。size正整數指定讀取多少字節
readlines(sizehint=0)
默認讀取全部內容,以列表形式返回。sizehint正整數指定讀取多少字節
示例:
1)請求URL
>>> import urllib, urllib2
>>> response = urllib.urlopen("http://www.baidu.com")   # 獲取的網站頁面源碼
>>> response.readline()
'<!DOCTYPE html>\n'
>>> response.getcode()
200
>>> response.geturl()
'http://www.baidu.com'
2)假裝chrome瀏覽器訪問
>>> user_agent = "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.157 Safari/537.36"
>>> header = {"User-Agent": user_agent}
>>> request = urllib2.Request("http://www.baidu.com", headers=header)     
>>> response = urllib2.urlopen(request)
>>> response.geturl()
'https://www.baidu.com/'
>>> print respose.info()  # 查看服務器返回的header信息
Server: bfe/1.0.8.18
Date: Sat, 12 Nov 2016 06:34:54 GMT
Content-Type: text/html; charset=utf-8
Transfer-Encoding: chunked
Connection: close
Vary: Accept-Encoding
Set-Cookie: BAIDUID=5979A74F742651531360C08F3BE06754:FG=1; expires=Thu, 31-Dec-37 23:55:55 GMT; max-age=2147483647; path=/; domain=.baidu.com
Set-Cookie: BIDUPSID=5979A74F742651531360C08F3BE06754; expires=Thu, 31-Dec-37 23:55:55 GMT; max-age=2147483647; path=/; domain=.baidu.com
Set-Cookie: PSTM=1478932494; expires=Thu, 31-Dec-37 23:55:55 GMT; max-age=2147483647; path=/; domain=.baidu.com
Set-Cookie: BDSVRTM=0; path=/
Set-Cookie: BD_HOME=0; path=/
Set-Cookie: H_PS_PSSID=1426_18240_17945_21118_17001_21454_21408_21394_21377_21525_21192; path=/; domain=.baidu.com
P3P: CP=" OTI DSP COR IVA OUR IND COM "
Cache-Control: private
Cxy_all: baidu+a24af77d41154f5fc0d314a73fd4c48f
Expires: Sat, 12 Nov 2016 06:34:17 GMT
X-Powered-By: HPHP
X-UA-Compatible: IE=Edge,chrome=1
Strict-Transport-Security: max-age=604800
BDPAGETYPE: 1
BDQID: 0xf51e0c970000d938
BDUSERID: 0
Set-Cookie: __bsi=12824513216883597638_00_24_N_N_3_0303_C02F_N_N_N_0; expires=Sat, 12-Nov-16 06:34:59 GMT; domain=www.baidu.com; path=/
3)提交用戶表單
>>> post_data = {"loginform-username":"test","loginform-password":"123456"}
>>> response = urllib2.urlopen("http://home.51cto.com/index", data=(urllib.urlencode(post_data)))
>>> response.read() # 登陸後網頁內容
提交用戶名和密碼錶單登陸到51cto網站,鍵是表單元素的id。其中用到了urlencode()方法,上面講過是用於轉爲字典格式爲URL接受的編碼格式。
例如:
>>> urllib.urlencode(post_data)
'loginform-password=123456&loginform-username=test'
4)保存cookie到變量中
#!/usr/bin/python
# -*- coding: utf-8 -*-
import urllib, urllib2
import cookielib
 
# 實例化CookieJar對象來保存cookie
cookie = cookielib.CookieJar()
# 建立cookie處理器
handler = urllib2.HTTPCookieProcessor(cookie)
# 經過handler構造opener
opener = urllib2.build_opener(handler)
response = opener.open("http://www.baidu.com")
for item in cookie:
    print item.name, item.value
 
# python test.py
BAIDUID EB4BF619C95630EFD619B99C596744B0:FG=1
BIDUPSID EB4BF619C95630EFD619B99C596744B0
H_PS_PSSID 1437_20795_21099_21455_21408_21395_21377_21526_21190_21306
PSTM 1478936429
BDSVRTM 0
BD_HOME 0
urlopen()自己就是一個opener,沒法知足對Cookie處理,全部就要新構造一個opener。這裏用到了cookielib庫,cookielib庫是一個可存儲cookie的對象。CookieJar類來捕獲cookie。
cookie存儲在客戶端,用來跟蹤瀏覽器用戶身份的會話技術。
5)保存cookie到文件
#!/usr/bin/python
# -*- coding: utf-8 -*-
import urllib, urllib2
import cookielib
 
cookie_file = 'cookie.txt'
# 保存cookie到文件
cookie = cookielib.MozillaCookieJar(cookie_file)
# 建立cookie處理器
handler = urllib2.HTTPCookieProcessor(cookie)
# 經過handler構造opener
opener = urllib2.build_opener(handler)
response = opener.open("http://www.baidu.com")
# 保存
cookie.save(ignore_discard=True, ignore_expires=True)  # ignore_discard默認是false,不保存將被丟失的。ignore_expires默認flase,若是cookie存在,則不寫入。
 
# python test.py
# cat cookie.txt
 
# Netscape HTTP Cookie File
# http://curl.haxx.se/rfc/cookie_spec.html
# This is a generated file!  Do not edit.
 
.baidu.com    TRUE    /    FALSE    3626420835    BAIDUID    687544519EA906BD0DE5AE02FB25A5B3:FG=1
.baidu.com    TRUE    /    FALSE    3626420835    BIDUPSID    687544519EA906BD0DE5AE02FB25A5B3
.baidu.com    TRUE    /    FALSE        H_PS_PSSID    1420_21450_21097_18560_21455_21408_21395_21377_21526_21192_20927
.baidu.com    TRUE    /    FALSE    3626420835    PSTM    1478937189
www.baidu.com    FALSE    /    FALSE        BDSVRTM    0
www.baidu.com    FALSE    /    FALSE        BD_HOME    0
MozillaCookieJar()這個類用來保存cookie到文件。
6)使用cookie訪問URL
#!/usr/bin/python
# -*- coding: utf-8 -*-
import urllib2
import cookielib
 
# 實例化對象
cookie = cookielib.MozillaCookieJar()
# 從文件中讀取cookie
cookie.load("cookie.txt", ignore_discard=True, ignore_expires=True)
# 建立cookie處理器
handler = urllib2.HTTPCookieProcessor(cookie)
# 經過handler構造opener
opener = urllib2.build_opener(handler)
# request = urllib2.Request("http://www.baidu.com")
response = opener.open("http://www.baidu.com")
7)使用代理服務器訪問URL
import urllib2
proxy_address = {"http": "http://218.17.252.34:3128"}
handler = urllib2.ProxyHandler(proxy_address)
opener = urllib2.build_opener(handler)
response = opener.open("http://www.baidu.com")
print response.read()
8)URL訪問認證
import urllib2
auth = urllib2.HTTPBasicAuthHandler()
# (realm, uri, user, passwd)
auth.add_password(None, 'http://www.example.com','user','123456')
opener = urllib2.build_opener(auth)
response = opener.open('http://www.example.com/test.html')
10 .14 json
JSON是一種輕量級數據交換格式,通常API返回的數據大可能是JSON、XML,若是返回JSON的話,將獲取的數據轉換成字典,方面在程序中處理。
json庫常常用的有兩種方法dumps和loads():
# 將字典轉換爲JSON字符串
>>> dict = {'user':[{'user1': 123}, {'user2': 456}]}
>>> type(dict)
<type 'dict'>
>>> json_str = json.dumps(dict)
>>> type(json_str)
<type 'str'>
# 把JSON字符串轉換爲字典
>>> d = json.loads(json_str)
>>> type(d)
<type 'dict'>
 JSON與Python解碼後數據類型:
JSON
Python
 object
dict
array
list
string
unicode
number(int)
init,long
number(real)
float
true
Ture
false
False
null
None
10.15 time
這個time庫提供了各類操做時間值。
方法
描述
示例
time.asctime([tuple])
將一個時間元組轉換成一個可讀的24個時間字符串
>>> time.asctime(time.localtime())
'Sat Nov 12 01:19:00 2016'
time.ctime(seconds)
字符串類型返回當前時間
>>> time.ctime()
'Sat Nov 12 01:19:32 2016'
time.localtime([seconds])
默認將當前時間轉換成一個(struct_timetm_year,tm_mon,tm_mday,tm_hour,tm_min,
                              tm_sec,tm_wday,tm_yday,tm_isdst)
>>> time.localtime()
time.struct_time(tm_year=2016, tm_mon=11, tm_mday=12, tm_hour=1, tm_min=19, tm_sec=56, tm_wday=5, tm_yday=317, tm_isdst=0)
time.mktime(tuple)
將一個struct_time轉換成時間戳
>>> time.mktime(time.localtime())
1478942416.0
time.sleep(seconds)
延遲執行給定的秒數
>>> time.sleep(1.5)
time.strftime(format[, tuple])
將元組時間轉換成指定格式。[tuple]不指定默認以當前時間
>>> time.strftime('%Y-%m-%d %H:%M:%S')
'2016-11-12 01:20:54'
time.time()
返回當前時間時間戳
>>> time.time()
1478942466.45977
strftime():
指令
描述
%a
簡化星期名稱,如Sat
%A
完整星期名稱,如Saturday
%b
簡化月份名稱,如Nov
%B
完整月份名稱,如November
%c
當前時區日期和時間
%d
%H
24小時制小時數(0-23)
%I
12小時制小時數(01-12)
%j
365天中第多少天
%m
%M
分鐘
%p
AM或PM,AM表示上午,PM表示下午
%S
%U
一年中第幾個星期
%w
星期幾
%W
一年中第幾個星期
%x
本地日期,如'11/12/16'
%X
本地時間,如'17:46:20'
%y
簡寫年名稱,如16
%Y
完全年名稱,如2016
%Z
當前時區名稱(PST:太平洋標準時間)
%%
表明一個%號自己
10.16 datetime
datetime庫提供瞭如下幾個類:
描述
datetime.date()
日期,年月日組成
datetime.datetime()
包括日期和時間
datetime.time()
時間,時分秒及微秒組成
datetime.timedelta()
時間間隔
datetime.tzinfo()
 
datetime.date()類:
方法
描述
描述
date.max
對象所能表示的最大日期
datetime.date(9999, 12, 31)
date.min
對象所能表示的最小日期
datetime.date(1, 1, 1)
date.strftime()
根據datetime自定義時間格式
>>> date.strftime(datetime.now(), '%Y-%m-%d %H:%M:%S')
'2016-11-12 07:24:15
date.today()
返回當前系統日期
>>> date.today()
datetime.date(2016, 11, 12)
date.isoformat()
返回ISO 8601格式時間(YYYY-MM-DD)
>>> date.isoformat(date.today())
'2016-11-12'
date.fromtimestamp()
根據時間戳返回日期
>>> date.fromtimestamp(time.time())
datetime.date(2016, 11, 12)
date.weekday()
根據日期返回星期幾,週一是0,以此類推
>>> date.weekday(date.today())
5
date.isoweekday()
根據日期返回星期幾,週一是1,以此類推
>>> date.isoweekday(date.today())
6
date.isocalendar()
根據日期返回日曆(年,第幾周,星期幾)
>>> date.isocalendar(date.today())
(2016, 45, 6)
datetime.datetime()類:
方法
描述
示例
datetime.now()/datetime.today()
獲取當前系統時間
>>> datetime.now()
datetime.datetime(2016, 11, 12, 7, 39, 35, 106385)
date.isoformat()
返回ISO 8601格式時間
>>> datetime.isoformat(datetime.now())
'2016-11-12T07:42:14.250440'
datetime.date()
返回時間日期對象,年月日
>>> datetime.date(datetime.now())
datetime.date(2016, 11, 12)
datetime.time()
返回時間對象,時分秒
>>> datetime.time(datetime.now())                   
datetime.time(7, 46, 2, 594397) 
datetime.utcnow()
UTC時間,比中國時間快8個小時
>>> datetime.utcnow()
datetime.datetime(2016, 11, 12, 15, 47, 53, 514210)
datetime.time()類:
方法
描述
示例
time.max
所能表示的最大時間
>>> time.max
datetime.time(23, 59, 59, 999999)
time.min
所能表示的最小時間
>>> time.min
datetime.time(0, 0)
time.resolution
時間最小單位,1微妙
>>> time.resolution
datetime.timedelta(0, 0, 1)
datetime.timedelta()類:
# 獲取昨天日期
>>> date.today() - timedelta(days=1)         
datetime.date(2016, 11, 11)
>>> date.isoformat(date.today() - timedelta(days=1))
'2016-11-11'
# 獲取明天日期
>>> date.today() + timedelta(days=1)               
datetime.date(2016, 11, 13)
>>> date.isoformat(date.today() + timedelta(days=1))
'2016-11-13'

 

第十一章 Python經常使用內建函數

內建函數,能夠直接使用,而不須要import。
在前面章節學過的sorded()、reversed()、range(),filter()、reduce()、map()等內建函數,下面再回顧下及學習一些新的內置函數。
 
函數
描述
示例
sorded(iterable, cmp=None, key=None, reverse=False)
正序排序可迭代對象,生成新的列表
>>> lst = [2,3,4,1,5]
>>> sorted(lst)
[1, 2, 3, 4, 5]
對字典value排序:
>>> dict = {'a':86, 'b':23, 'c':45}                                 
>>> sorted(dict.iteritems(), key=lambda x:x[1], reverse=True)
[('a', 86), ('c', 45), ('b', 23)]
reversed(sequence)
反向排序序列,返回一個可迭代對象
>>> lst = [1,2,3,4,5]
>>> lst2 = []
>>> for i in reversed(lst):
...    lst2.append(i)
...
>>> lst2
[5, 4, 3, 2, 1]
range(start, stop[, step])
生成整數列表
>>> range(0,5)
[0, 1, 2, 3, 4]
>>> range(0,5, 2) 
[0, 2, 4]
xrange(start, stop[, step])
生成可迭代對象,比range節省內存資源
>>> type(xrange(0,5))
<type 'xrange'>
>>> for i in xrange(0,5):
...   print i
...
0
1
2
3
4
filter(function or None, sequence)
將序列中的元素經過函數處理返回一個新列表、元組或字符串
例如:過濾列表中的奇數
>>> lst = [1,2,3,4,5]
>>> filter(lambda x:x%2==0, lst)
[2, 4]
reduce(function, sequence[, initial])
二元運算函數,因此只接受二元操做函數
例如:計算列表總和
>>> lst = [1,2,3,4,5]
>>> reduce(lambda x,y:x+y, lst)
15
先將前兩個元素相加等於3,再把結果與第三個元素相加等於6,以此類推
map(function, sequence[, sequence, ...])
將序列中的元素經過函數處理返回一個新列表
>>> lst = [1,2,3,4,5]
>>> map(lambda x:str(x)+".txt", lst)
['1.txt', '2.txt', '3.txt', '4.txt', '5.txt']
len(object)
返回序列的數量
>>> len([1,2,3])
3
abs(number)
返回參數的絕對值
>>> abs(-2)
2
eval(source[, globals[, locals]])
把字符串當成Python表達式處理並返回計算結果
>>> a = '1 + 2'
>>> eval(a)
3
repr(object)
把象轉爲字符串表示
>>> repr(3)
'3'
>>> repr('1+2')
"'1+2'"
round(number[, ndigits])
number四捨五入計算,返回浮點數。ndigits是保留幾位小數
>>> round(1.6)
2.0
min(iterable[, key=func])
min(a, b, c, ...[, key=func]
返回最小項。能夠是可迭代對象,也能夠是兩個或兩個以上參數。
>>> min([1,2,3])
1
>>> min('a', 'b', 'c')
'a'
max(iterable[, key=func])
max(a, b, c, ...[, key=func])
返回最大項。與min使用方法同樣。
 
sum(sequence[, start])
返回序列合,start在計算結果上加的數
>>> sum([1,2,3])
6
isinstance(object, class-or-type-or-tuple)
判斷object類型,返回布爾值
>>> isinstance([1,2,3],list)
True
>>> isinstance([1,2,3],tuple)
False
hex(number)
返回整數十六進制表示
>>> hex(18)
'0x12'
zip(seq1 [, seq2 [...]])
返回一個合併的列表元組,每一個元組裏面是每一個seq對應的下標值,在長度最短的seq結束。
>>> zip(range(5),['a','b','c'])
[(0, 'a'), (1, 'b'), (2, 'c')]
cmp(x, y)
比較兩個對象,x==y等於返回0,x>y返回整數,x<y返回負數
>>> cmp(1,1)
0
>>> cmp(1,2)
-1
>>> cmp(1,0)
1
locals()
返回當前局部變量字典
>>> a = 1
>>> b = 2
>>> locals()
{'a': 1, 'b': 2,......
 
內置函數還有不少,有興趣能夠參考一下: https://docs.python.org/2/library/functions.html

博客地址:http://lizhenliang.blog.51cto.com and https://yq.aliyun.com/u/lizhenliang
 

第十二章 Python文件操做

12.1 open()

open()函數做用是打開文件,返回一個文件對象。
用法格式:open(name[, mode[, buffering[,encoding]]]) -> file object
name 文件名
mode 模式,好比以只讀方式打開
buffering 緩衝區
encoding 返回數據採用的什麼編碼,通常utf8或gbk
Mode
Description
r
只讀,默認
w
只寫,打開前清空文件內容
a
追加
a+
讀寫,寫到文件末尾
w+
可讀寫,清空文件內容
r+
可讀寫,能寫到文件任何位置
rb
二進制模式讀
wb
二進制模式寫,清空文件內容
例如:打開一個文件
>>> f = open('test.txt', 'r')
>>> f.
f.__class__(         f.__new__(           f.encoding           f.readinto(
f.__delattr__(       f.__reduce__(        f.errors             f.readline(
f.__doc__            f.__reduce_ex__(     f.fileno(            f.readlines(
f.__enter__(         f.__repr__(          f.flush(             f.seek(
f.__exit__(          f.__setattr__(       f.isatty(            f.softspace
f.__format__(        f.__sizeof__(        f.mode               f.tell(
f.__getattribute__(  f.__str__(           f.name               f.truncate(
f.__hash__(          f.__subclasshook__(  f.newlines           f.write(
f.__init__(          f.close(             f.next(              f.writelines(
f.__iter__(          f.closed             f.read(              f.xreadlines(
open()函數打開文件返回一個文件對象,並賦予遍歷f,f就擁有了這個文件對象的操做方法。
方法
描述
f.read([size])
讀取size字節,當未指定或給負值時,讀取剩餘全部的字節,做爲字符串返回
f.readline([size])
從文件中讀取下一行,做爲字符串返回。若是指定size則返回size字節
f.readlines([size])
讀取size字節,當未指定或給負值時,讀取剩餘全部的字節,做爲列表返回
f.write(str)
寫字符串到文件
f.writelines(seq)
寫序列到文件,seq必須是一個可迭代對象,並且要是一個字符串序列
f.seek(offset[, whence=0])
在文件中移動文件指針,從whence(0表明文件起始位置,默認。1表明當前位置。2表明文件末尾)偏移offset個字節
f.tell()
返回當前在文件中的位置
f.close()
關閉文件
f.flush
刷新緩衝區到磁盤

博客地址:http://lizhenliang.blog.51cto.com and https://yq.aliyun.com/u/lizhenliang
QQ羣:323779636(Shell/Python運維開發羣)


12.2 文件對象操做
寫一個測試文件test.txt舉例:
# cat test.txt
1.Python
2.Java
3.C++
4.Ruby
12.2.1 read()讀取全部內容
>>> f = open('test.txt', 'r')
>>> f.read()
'1.Python\n2.Java\n3.C++\n4.Ruby\n'
# 獲取指定字節
 指定讀取多少字節:
>>> f = open('test.txt', 'r')
>>> f.read(9)
'1.Python\n'
12.2.2 readline()讀取下一行內容
>>> f = open('test.txt', 'r')
>>> f.readline()
'1.Python\n'
>>> f.readline()
'2.Java\n'
12.2.3 readlines()讀取全部內容返回一個列表
>>> f = open('test.txt', 'r')
>>> f.readlines()
['1.Python\n', '2.Java\n', '3.C++\n', '4.Ruby\n']
12.2.4 wirte()寫入字符串到文件
>>> f = open('test.txt', 'a')  # 以追加方式打開文件
>>> f.write("5.Shell\n")  # 這一步並無真正寫到文件
>>> f.flush()  # 刷新到磁盤才寫到文件
# cat test.txt
1.Python
2.Java
3.C++
4.Ruby
5.Shell
12.2.5 wirtelines()寫入一個序列字符串到文件
>>> f = open('test.txt', 'a')
>>> f.writelines(['a','b','c'])
>>> f.flush()
# cat test.txt
1.Python
2.Java
3.C++
4.Ruby
5.Shell
abc
12.2.6 seek()從指定位置讀取
>>> f = open('test.txt', 'r')
>>> f.tell()
0
>>> f.seek(9)
>>> f.tell()
9
>>> f.seek(5,1)  # 1表示從當前位置開始
>>> f.tell()
14
12.2.7 tell()返回當前指針位置
>>> f = open('test.txt', 'r')
>>> f.tell()
0
>>> f.readline()
'1.Python\n'
>>> f.tell()   
9
>>> f.readline()
'2.Java\n'
>>> f.tell()   
16
>>> f.close()  # 使用完後關閉文件
12.3 文件對象增刪改查
在shell中,咱們要想對文件指定行插入內容、替換等狀況,使用sed工具很容易就實現。在本章節講的open()函數並無直接相似與sed工具的方法,要想實現這樣的操做,變通的處理能到達此效果,主要思路是先讀取內容修改,再寫會文件,如下舉幾個經常使用的狀況
12.3.1 在第一行增長一行
例如:在開頭添加一個test字符串
#!/usr/bin/python
# -*- coding: utf-8 -*-
f = open('test.txt', 'r')
data = f.read()
data = "test\n" + data
f = open('test.txt', 'w')
f.write(data)
f.flush()
f.close()
 
# python test.py
# cat test.txt
test
1.Python
2.Java
3.C++
4.Ruby
先將數據讀出來,而後把要添加的test字符串拼接到原有的數據,而後在寫入這個文件。
12.3.2 在指定行添加一行
例如:在第二行添加一個test字符串
#!/usr/bin/python
# -*- coding: utf-8 -*-
f = open('test.txt', 'r')
data_list = f.readlines()  # 經測試,此方法比下面迭代效率高
# data_list = []
# for line in f:
#     data_list.append(line)
 
data_list.insert(1, 'test\n')
# data = ''.join(data)
f = open('test.txt', 'w')
# f.write(data)
f.writelines(data_list)  
f.flush()
f.close
 
# python test.py
# cat test.txt
1.Python
test
2.Java
3.C++
4.Ruby
先將數據以列表存儲,就能夠根據下標插入到指定位置,也就是哪一行了。再經過join把列表拼接成字符串,最後寫到文件。
12.3.3 在匹配行前一行或後一行添加test字符串
#!/usr/bin/python
# -*- coding: utf-8 -*-
f = open('test.txt', 'r')
data_list = f.readlines()
data_list.insert(2-1, 'test\n')  # 在指定行減去一行就是上一行了,下一行插入同理
f = open('test.txt', 'w')
f.writelines(data_list)
f.flush()
f.close
12.3.4 刪除指定行
例如:刪除第三行,與在指定行添加同理
#!/usr/bin/python
# -*- coding: utf-8 -*-
f = open('test.txt', 'r')
data_list = f.readlines()
 
data_list.pop(2)
f = open('test.txt', 'w')
f.writelines(data_list)
f.flush()
f.close
例如:只保留第一行至第三行
#!/usr/bin/python
# -*- coding: utf-8 -*-
f = open('test.txt', 'r')
data_list = f.readlines()[0:2]  # 列表切片
f = open('test.txt', 'w')
f.write(data_list)
f.flush()
f.close
12.3.5 刪除匹配行
例如:刪除匹配Py字符的行
#!/usr/bin/python
# -*- coding: utf-8 -*-
f = open('test.txt', 'r')
data = f.readlines()
# data_list = []
# for line in data:
#     if line.find('Py') == -1:   # 若是當前行不包含Py字符,會返回-1,不然返回下標
#         data_list.append(line)
data_list = [line for line in data if line.find('Py') == -1]  
f = open('test.txt', 'w')
f.writelines(data_list)
f.flush()
f.close
12.3.6 全局替換字符串
#!/usr/bin/python
# -*- coding: utf-8 -*-
f = open('test.txt', 'r')
data = f.read()
data.replace('old string', 'new string')
f = open('test.txt', 'w')
f.write(data)
f.flush()
f.close
12.3.7 在指定行替換字符串
例如:將C++改成C#
#!/usr/bin/python
# -*- coding: utf-8 -*-
f = open('test.txt', 'r')
data = f.readlines()
data_list = []
for line in data:
    if data.index(line) == 2:
        data_list.append(line.replace('++', '#'))
    else:
        data_list.append(line)
f = open('test.txt', 'w')
f.writelines(data_list)
f.flush()
f.close
12.3.8 處理大文件
在讀取上G文件時,直接讀取全部內容會致使內存佔用過多,內存爆掉。要想提升處理效率,有如下兩種方法:
方法1:open()打開文件返回的對象自己就是可迭代的,利用for循環迭代可提升處理性能
>>> f = open('test.txt')
>>> for line in f:
...   print line   # 每行後面會有一個換行符\n,因此會打印出來換行符,能夠使用line.strip('\n')去除
...
1.Python
 
2.Java
 
3.C++
 
4.Ruby
方法2:每次只讀取固定字節
#!/usr/bin/python
# -*- coding: utf-8 -*-
f = open('test.txt')
while True:
    data = f.read(1024)  # 每次只讀取1024字節
    if not data: break
12.3.9 下載文件
方法1:
import urllib
url = "http://nginx.org/download/nginx-1.10.1.tar.gz"
urllib.urlretrieve(url, "nginx-1.10.1.tar.gz")
 
方法2:
import urllib2
url = "http://nginx.org/download/nginx-1.10.1.tar.gz"
f = urllib2.urlopen(url).read()
with open("nginx-1.10.1.tar.gz", "wb") as data:
    data.write(f)
 
12.4 fileinput
fileinput模塊是Python內建模塊,用於遍歷文件,可對多文件操做。
方法
描述
fileinput.input([files[, inplace[, backup[, mode[, openhook]]]]])
files:文件路徑,多文件這樣寫['1.txt,'2.txt'']
inplace:是否將標準輸出寫到原文件,默認是0,不寫
backup:備份文件擴展名,好比.bak
mode:讀寫模式,默認r,只讀
openhook:
fileinput.isfirstline()
檢查當前行是不是文件的第一行
fileinput.lineno()
返回當前已經讀取行的數量
fileinput.fileno()
返回當前文件數量
fileinput.filelineno()
返回當前讀取行的行號
fileinput.filename()
返回當前文件名
 
12.4.1 遍歷文件內容
#!/usr/bin/python
# -*- coding: utf-8 -*-
import fileinput
for line in fileinput.input('test.txt'):
    print line
 
# python test.py 
1.Python
 
2.Java
 
3.C++
 
4.Ruby
12.4.2 返回當前讀取行的行號
#!/usr/bin/python
# -*- coding: utf-8 -*-
import fileinput
for line in fileinput.input('test.txt'):
    print fileinput.filelineno()
    print line,  # 逗號忽略換行符
# python test.py
1
1.Python
2
2.Java
3
3.C++
4
4.Ruby
12.4.3 全局替換字符,修改原文件
#!/usr/bin/python
# -*- coding: utf-8 -*-
import fileinput
for line in fileinput.input('test.txt', backup='.bak', inplace=1):
    line = line.replace('++','#')
    print line,
先把要操做的文件備份一個以.bak的後綴文件,inplace=1是將標準輸出寫到原文件,也就是這個腳本若是沒有標準輸出,就會以空數據寫到原文件。
12.4.4 對多文件操做
#!/usr/bin/python
# -*- coding: utf-8 -*-
import fileinput
for line in fileinput.input(['test.txt', 'test2.txt']):
    print line,
12.4.5 實時讀取文件新增內容,相似tail -f
#!/usr/bin/python
# -*- coding: utf-8 -*-
with open('access.log') as f:
     f.seek(0,2)   # 每次打開文件都將文件指針移動到末尾
     while True:  
         line = f.readline()
         if line:
             print line,
這個死循環會一直執行下面的操做。很消耗性能。
咱們能夠加個休眠,每秒讀取一次:
#!/usr/bin/python
# -*- coding: utf-8 -*-
import time
with open('access.log') as f:
     f.seek(0,2)
     while True:
         line = f.readline()
         if line:
             print line,
         else:
             time.sleep(1)
12.5 shutil
shutil模塊是Python內建模塊,用於文件或目錄拷貝,歸檔。
方法
描述
shutil.copyfile(src, dst)
複製文件
shutil.copytree(src, dst)
複製文件或目錄
shutil.move(src, dst)
移動文件或目錄
shutil.rmtree(path,ignore_errors=False, onerror=None)
遞歸刪除目錄。os.rmdir()不能刪除有文件的目錄,就能夠用這個了
shutil.make_archive(base_name, format, root_dir=None, base_dir=None, verbose=0, dry_run=0, owner=None, group=None, logger=None)
Python2.7之後纔有這個方法。
功能是建立zip或tar歸檔文件。
base_name:要建立歸檔文件名
format:歸檔文件格式,有zip、tar、bztar、gztar
root_dir:要壓縮的目錄
base_dir:?
用法:shutil.make_archive('wp','zip','/root/wordpress')   
12.6 with語句
在處理一些事務時,可能會出現異常和後續的清理工做,好比讀取失敗,關閉文件等。這就用到了異常處理語句try...except,以下:
#!/usr/bin/python
# -*- coding: utf-8 -*-
f = open('test.txt')
try:
    data = f.read()
finally:
    f.close()
Python對於這種狀況提供了一種更簡單的處理方式,with語句。處理一個文件時,先獲取一個文件句柄,再從文件中讀取數據,最後關閉文件句柄。以下:
#!/usr/bin/python
# -*- coding: utf-8 -*-
with open('test.txt') as f:
    data = f.read()

可見這種方式顯得更簡約,一些異常、清理工做都交給with處理了。

 

第十三章 Python數據庫編程

本章節講解Python操做數據庫,完成簡單的增刪改查工做,以MySQL數據庫爲例。
Python的MySQL數據庫操做模塊叫MySQLdb,須要額外的安裝下。
經過pip工具安裝:pip install MySQLdb
MySQLdb模塊,咱們主要就用到鏈接數據庫的方法MySQLdb.Connect(),鏈接上數據庫後,再使用一些方法作相應的操做。
MySQLdb.Connect(parameters...)方法提供瞭如下一些經常使用的參數:
參數
描述
host
數據庫地址
user
數據庫用戶名,
passwd
數據庫密碼,默認爲空
db
數據庫庫名,沒有默認庫
port
數據庫端口,默認3306
connect_timeout
鏈接超時時間,秒爲單位
use_unicode
結果以unicode字符串返回
charset
插入數據庫編碼
鏈接對象返回的connect()函數:
commit()
提交事務。對支持事務的數據庫和表,若是提交修改操做,不適用這個方法,則不會寫到數據庫中
rollback()
事務回滾。對支持事務的數據庫和表,若是執行此方法,則回滾當前事務。在沒有commit()前提下。
cursor([cursorclass])
建立一個遊標對象。全部的sql語句的執行都要在遊標對象下進行。MySQL自己不支持遊標,MySQLdb模塊對其遊標進行了仿真。
遊標對象也提供了幾種方法:
close()
關閉遊標
execute(sql)
執行sql語句
excutemany(sql)
執行多條sql語句
fetchone()
從執行結果中取第一條記錄
fetchmany(n)
從執行結果中取n條記錄
fetchall()
從執行結果中取全部記錄
scroll(self, value, mode='relative')
遊標滾動

博客地址:http://lizhenliang.blog.51cto.com and https://yq.aliyun.com/u/lizhenliang
QQ羣:323779636(Shell/Python運維開發羣)


13.1 數據庫增刪改查
13.1.1 在test庫建立一張user表,並添加一條記錄
>>> conn = MySQLdb.Connect(host='192.168.1.244',user='root',passwd='QHyCTajI',db='test',charset='utf8')
>>> cursor = conn.cursor()
>>> sql = "create table user(id int,name varchar(30),password varchar(30))"
>>> cursor.execute(sql)   # 返回的數字是影響的行數
0L    
>>> sql = "insert into user(id,name,password) values('1','xiaoming','123456')"
>>> cursor.execute(sql)
1L
>>> conn.commit()  # 提交事務,寫入到數據庫
>>> cursor.execute('show tables')  # 查看建立的表
1L
>>> cursor.fetchall()  # 返回上一個遊標執行的全部結果,默認是以元組形式返回
((u'user',),)
>>> cursor.execute('select * from user')           
1L
>>> cursor.fetchall()
((1L, u'xiaoming', u'123456'),)
13.1.2 插入多條數據
>>> sql = 'insert into user(id,name,password) values(%s,%s,%s)'
>>> args = [('2','zhangsan','123456'), ('3','lisi','123456'),('4','wangwu','123456')] 
>>> cursor.executemany(sql, args)
3L
>>> conn.commit()
>>> sql = 'select * from user'
>>> cursor.execute(sql)
4L
>>> cursor.fetchall()
((1L, u'xiaoming', u'123456'), (2L, u'zhangsan', u'123456'), (3L, u'lisi', u'123456'), (4L, u'wangwu', u'123456'))
args變量是一個包含多元組的列表,每一個元組對應着每條記錄。當查詢多條記錄時,使用此方法,可有效提升插入效率。
13.1.3 刪除用戶名xiaoming的記錄
>>> sql = 'delete from user where name="xiaoming"'
>>> cursor.execute(sql)                           
1L
>>> conn.commit()
>>> sql = 'select * from user'                   
>>> cursor.execute(sql)       
3L
>>> cursor.fetchall()         
((2L, u'zhangsan', u'123456'), (3L, u'lisi', u'123456'), (4L, u'wangwu', u'123456'))
13.1.4 查詢記錄
>>> sql = 'select * from user' 
>>> cursor.execute(sql)         
3L
>>> cursor.fetchone()   # 獲取第一條記錄
(2L, u'zhangsan', u'123456')
>>> sql = 'select * from user' 
>>> cursor.execute(sql)         
3L
>>> cursor.fetchmany(2) # 獲取兩條記錄
((2L, u'zhangsan', u'123456'), (3L, u'lisi', u'123456'))
13.1.4 以字典形式返回結果
默認顯示是元組形式,要想返回字典形式,使得更易處理,就用到cursor([cursorclass])中的cusorclass參數。
傳入MySQLdb.cursors.DictCursor類:
>>> cursor = conn.cursor(MySQLdb.cursors.DictCursor)
>>> sql = 'select * from user'
>>> cursor.execute(sql)
3L
>>> cursor.fetchall()
({'password': u'123456', 'id': 2L, 'name': u'zhangsan'}, {'password': u'123456', 'id': 3L, 'name': u'lisi'}, {'password': u'123456', 'id': 4L, 'name': u'wangwu'})
13.2 遍歷查詢結果
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import MySQLdb
try:
    conn = MySQLdb.Connect(host='127.0.0.1', port=3306, user='root', passwd='123456', connect_timeout=3, charset='utf8')
    cursor = conn.cursor()
    sql = "select * from user"
    cursor.execute(sql)
    for i in cursor.fetchall():
        print i
except Exception, e:
    print ("Connection Error: " + str(e))
finally:
    conn.close()
 
# python test.py
(2L, u'zhangsan', u'123456')
(3L, u'lisi', u'123456')
(4L, u'wangwu', u'123456')
使用for循環遍歷查詢結果,並增長了異常處理。

 

第十四章 Python發送郵件(常見四種郵件內容)

在寫腳本時,放到後臺運行,想知道執行狀況,會經過郵件、SMS(短信)、飛信、微信等方式通知管理員,用的最多的是郵件。在linux下,Shell腳本發送郵件告警是件很簡單的事,有現成的郵件服務軟件或者調用運營商郵箱服務器。
對於Python來講,須要編寫腳本調用郵件服務器來發送郵件,使用的協議是SMTP。接收郵件,使用的協議是POP3和IMAP。我想有必要說明下 ,POP3和IMAP的區別:POP3在客戶端郵箱中所作的操做不會反饋到郵箱服務器,好比刪除一封郵件,郵箱服務器並不會刪除。IMAP則會反饋到郵箱服務器,會作相應的操做。
Python分別提供了收發郵件的庫,smtplib、poplib和imaplib。
本章主要講解若是使用smtplib庫實現發送各類形式的郵件內容。在smtplib庫中,主要主要用smtplib.SMTP()類,用於鏈接SMTP服務器,發送郵件。
這個類有幾個經常使用的方法:
方法
描述
SMTP.set_debuglevel(level)
設置輸出debug調試信息,默認不輸出
SMTP.docmd(cmd[, argstring])
發送一個命令到SMTP服務器
SMTP.connect([host[, port]])
鏈接到指定的SMTP服務器
SMTP.helo([hostname])
使用helo指令向SMTP服務器確認你的身份
SMTP.ehlo(hostname)
使用ehlo指令像ESMTP(SMTP擴展)確認你的身份
SMTP.ehlo_or_helo_if_needed()
若是在之前的會話鏈接中沒有提供ehlo或者helo指令,這個方法會調用ehlo()或helo()
SMTP.has_extn(name)
判斷指定名稱是否在SMTP服務器上
SMTP.verify(address)
判斷郵件地址是否在SMTP服務器上
SMTP.starttls([keyfile[, certfile]])
使SMTP鏈接運行在TLS模式,全部的SMTP指令都會被加密
SMTP.login(user, password)
登陸SMTP服務器
SMTP.sendmail(from_addr, to_addrs, msg, mail_options=[], rcpt_options=[])
發送郵件
from_addr:郵件發件人
to_addrs:郵件收件人
msg:發送消息
SMTP.quit()
關閉SMTP會話
SMTP.close()
關閉SMTP服務器鏈接
看下官方給的示例:
>>> import smtplib
>>> s=smtplib.SMTP("localhost")
>>> tolist=["one@one.org","two@two.org","three@three.org","four@four.org"]
>>> msg = '''\
     ... From: Me@my.org
     ... Subject: testin'...
     ...
     ... This is a test '''
>>> s.sendmail("me@my.org",tolist,msg)
     { "three@three.org" : ( 550 ,"User unknown" ) }
>>> s.quit()
咱們根據示例給本身發一個郵件測試下:
我這裏測試使用本地的SMTP服務器,也就是要裝一個支持SMTP協議的服務,好比sendmail、postfix等。CentOS安裝sendmail:yum install sendmail
>>> import smtplib
>>> s = smtplib.SMTP("localhost")
>>> tolist = ["xxx@qq.com", "xxx@163.com"]
>>> msg = '''\
... From: Me@my.org
... Subject: test
... This is a test '''
>>> s.sendmail("me@my.org", tolist, msg)
{}
進入騰訊和網易收件人郵箱,就能看到剛發的測試郵件,通常都被郵箱服務器過濾成垃圾郵件,因此收件箱沒有,你要去垃圾箱看看。
能夠看到,多個收件人能夠放到一個列表中進行羣發。msg對象裏From表示發件人,Subject是郵件標題,換行後輸入的是郵件內容。
上面是使用本地SMTP服務器發送的郵件,測試下用163服務器發送郵件看看效果:
>>> import smtplib
>>> s = smtplib.SMTP("smtp.163.com")
>>> s.login("baojingtongzhi@163.com", "xxx")
(235, 'Authentication successful')
>>> tolist = ["xxx@qq.com", "xxx@163.com"]
>>> msg = '''\
... From: baojingtongzhi@163.com
... Subject: test
... This is a test '''
>>> s.sendmail("baojingtongzhi@163.com", tolist, msg)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib64/python2.6/smtplib.py", line 725, in sendmail
    raise SMTPDataError(code, resp)
smtplib.SMTPDataError: (554, 'DT:SPM 163 smtp10,DsCowAAXIdDIJAtYkZiTAA--.65425S2 1477125592,please see http://mail.163.com/help/help_spam_16.htm?ip=119.57.73.67&hostid=smtp10&time=1477125592')
訪問給出的163網址,SMTP554錯誤是: "554 DT:SUM 信封發件人和信頭髮件人不匹配;"
大概已經明白啥意思,看上面再使用本地SMTP服務器時候,收件人位置是「undisclosed-recipients」,看這樣163的SMTP服務器不給咱們服務的緣由就是這裏收件人沒指定。
從新修改下msg對象,添加上收件人:
>>> msg = '''\           
... From: baojingtongzhi@163.com
... To: 962510244@qq.com ,zhenliang369@163.com
... Subject: test
...
... This is a test '''
>>> s.sendmail("baojingtongzhi@163.com", tolist, msg)
{}
好了,能夠正常發送郵件了。msg這個格式是SMTP規定的,必定要遵照。
14.1 Python發送郵件並抄送
#!/usr/bin/python
# -*- coding: utf-8 -*-
import smtplib
 
def sendMail(body):
    smtp_server = 'smtp.163.com'
    from_mail = 'baojingtongzhi@163.com'
    mail_pass = 'xxx'
    to_mail = ['962510244@qq.com', 'zhenliang369@163.com']
    cc_mail = ['lizhenliang@xxx.com']
    from_name = 'monitor' 
    subject = u'監控'.encode('gbk')   # 以gbk編碼發送,通常郵件客戶端都能識別
 
#     msg = '''\
# From: %s <%s>
# To: %s
# Subject: %s
 
# %s''' %(from_name, from_mail, to_mail_str, subject, body)  # 這種方式必須將郵件頭信息靠左,也就是每行開頭不能用空格,不然報SMTP 554
 
    mail = [
        "From: %s <%s>" % (from_name, from_mail),
        "To: %s" % ','.join(to_mail),   # 轉成字符串,以逗號分隔元素
        "Subject: %s" % subject,
        "Cc: %s" % ','.join(cc_mail),
        "",
        body
        ]
    msg = '\n'.join(mail)  # 這種方式先將頭信息放到列表中,而後用join拼接,並以換行符分隔元素,結果就是和上面註釋同樣了
 
    try:
        s = smtplib.SMTP()
        s.connect(smtp_server, '25')
        s.login(from_mail, mail_pass)
        s.sendmail(from_mail, to_mail+cc_mail, msg)   
        s.quit()
    except smtplib.SMTPException as e:
        print "Error: %s" %e
if __name__ == "__main__":
    sendMail("This is a test!")
s.sendmail(from_mail, to_mail+cc_mail, msg) 在這裏注意下,收件人和抄送人爲何放一塊兒發送呢?其實不管是收件人仍是抄送人,它們收到的郵件都是同樣的,SMTP都是認爲收件人這樣一封一封的發出。因此實際上並無抄送這個概念,只是在郵件頭加了抄送人的信息罷了!另外,若是不須要抄送人,直接把上面cc的信息去掉便可。
14.2 Python發送郵件帶附件
因爲SMTP.sendmail()方法不支持添加附件,因此能夠使用email模塊來知足需求。email模塊是一個構造郵件和解析郵件的模塊。
先看下如何用email庫構造一個簡單的郵件:
message = Message()
message['Subject'] = '郵件主題'
message['From'] = from_mail
message['To'] = to_mail
message['Cc'] = cc_mail
message.set_payload('郵件內容')
基本的格式就是這樣的!
繼續回到主題,發送郵件帶附件:
#!/usr/bin/python
# -*- coding: utf-8 -*-
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.header import Header
from email import encoders
from email.mime.base import MIMEBase
from email.utils import parseaddr, formataddr
 
# 格式化郵件地址
def formatAddr(s):
    name, addr = parseaddr(s)
    return formataddr((Header(name, 'utf-8').encode(), addr))
 
def sendMail(body, attachment):
    smtp_server = 'smtp.163.com'
    from_mail = 'baojingtongzhi@163.com'
    mail_pass = 'xxx'
    to_mail = ['962510244@qq.com', 'zhenliang369@163.com']
 
    # 構造一個MIMEMultipart對象表明郵件自己
    msg = MIMEMultipart()
    # Header對中文進行轉碼
    msg['From'] = formatAddr('管理員 <%s>' % from_mail).encode()
    msg['To'] = ','.join(to_mail)
    msg['Subject'] = Header('監控', 'utf-8').encode()
 
    # plain表明純文本
    msg.attach(MIMEText(body, 'plain', 'utf-8'))
 
    # 二進制方式模式文件
    with open(attachment, 'rb') as f:
        # MIMEBase表示附件的對象
        mime = MIMEBase('text', 'txt', filename=attachment)
        # filename是顯示附件名字
        mime.add_header('Content-Disposition', 'attachment', filename=attachment)
        # 獲取附件內容
        mime.set_payload(f.read())
        encoders.encode_base64(mime)
        # 做爲附件添加到郵件
        msg.attach(mime)
    try:
        s = smtplib.SMTP()
        s.connect(smtp_server, "25")
        s.login(from_mail, mail_pass)
        s.sendmail(from_mail, to_mail, msg.as_string())  # as_string()把MIMEText對象變成str
        s.quit()
    except smtplib.SMTPException as e:
        print "Error: %s" % e
if __name__ == "__main__":
    sendMail('附件是測試數據, 請查收!', 'test.txt')

博客地址:http://lizhenliang.blog.51cto.com and https://yq.aliyun.com/u/lizhenliang
QQ羣:323779636(Shell/Python運維開發羣)

14.3 Python發送HTML郵件
#!/usr/bin/python
# -*- coding: utf-8 -*-
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.header import Header
from email.utils import parseaddr, formataddr
 
# 格式化郵件地址
def formatAddr(s):
    name, addr = parseaddr(s)
    return formataddr((Header(name, 'utf-8').encode(), addr))
 
def sendMail(body):
    smtp_server = 'smtp.163.com'
    from_mail = 'baojingtongzhi@163.com'
    mail_pass = 'xxx'
    to_mail = ['962510244@qq.com', 'zhenliang369@163.com']
 
    # 構造一個MIMEMultipart對象表明郵件自己
    msg = MIMEMultipart() 
    # Header對中文進行轉碼
    msg['From'] = formatAddr('管理員 <%s>' % from_mail).encode()
    msg['To'] = ','.join(to_mail)
    msg['Subject'] = Header('監控', 'utf-8').encode()
    msg.attach(MIMEText(body, 'html', 'utf-8'))
 
    try:
        s = smtplib.SMTP()     
        s.connect(smtp_server, "25")   
        s.login(from_mail, mail_pass)
        s.sendmail(from_mail, to_mail, msg.as_string())  # as_string()把MIMEText對象變成str     
        s.quit()
    except smtplib.SMTPException as e:
        print "Error: %s" % e
if __name__ == "__main__":
    body = """
    <h1>測試郵件</h1>
    <h2 style="color:red">This is a test</h1>
    """
    sendMail(body)
14.4 Python發送圖片郵件
#!/usr/bin/python
# -*- coding: utf-8 -*-
import smtplib
from email.mime.text import MIMEText
from email.mime.image import MIMEImage
from email.mime.multipart import MIMEMultipart
from email.header import Header
from email.utils import parseaddr, formataddr
 
# 格式化郵件地址
def formatAddr(s):
    name, addr = parseaddr(s)
    return formataddr((Header(name, 'utf-8').encode(), addr))
 
def sendMail(body, image):
    smtp_server = 'smtp.163.com'
    from_mail = 'baojingtongzhi@163.com'
    mail_pass = 'xxx'
    to_mail = ['962510244@qq.com', 'zhenliang369@163.com']
 
    # 構造一個MIMEMultipart對象表明郵件自己
    msg = MIMEMultipart() 
    # Header對中文進行轉碼
    msg['From'] = formatAddr('管理員 <%s>' % from_mail).encode()
    msg['To'] = ','.join(to_mail)
    msg['Subject'] = Header('監控', 'utf-8').encode()
    msg.attach(MIMEText(body, 'html', 'utf-8'))
 
    # 二進制模式讀取圖片
    with open(image, 'rb') as f:
        msgImage = MIMEImage(f.read())
 
    # 定義圖片ID
    msgImage.add_header('Content-ID', '<image1>')
    msg.attach(msgImage)
 
    try:
        s = smtplib.SMTP()     
        s.connect(smtp_server, "25")   
        s.login(from_mail, mail_pass)
        s.sendmail(from_mail, to_mail, msg.as_string())  # as_string()把MIMEText對象變成str     
        s.quit()
    except smtplib.SMTPException as e:
        print "Error: %s" % e
if __name__ == "__main__":
    body = """
    <h1>測試圖片</h1>
    <img src="cid:image1"/>    # 引用圖片
    """
    sendMail(body, 'test.png')
上面發郵件的幾種常見的發郵件方法基本知足平常需求了。

 

第十五章 Python多進程與多線程

15.1 multiprocessing
multiprocessing是多進程模塊,多進程提供了任務併發性,能充分利用多核處理器。避免了GIL(全局解釋鎖)對資源的影響。
有如下經常使用類:
描述
Process(group=None, target=None, name=None, args=(), kwargs={})
派生一個進程對象,而後調用start()方法啓動
 
Pool(processes=None, initializer=None, initargs=())
返回一個進程池對象,processes進程池進程數量
Pipe(duplex=True)
返回兩個鏈接對象由管道鏈接
Queue(maxsize=0)
返回隊列對象,操做方法跟Queue.Queue同樣
multiprocessing.dummy
這個庫是用於實現多線程
Process()類有如下些方法:
run()
 
start()
啓動進程對象
join([timeout])
等待子進程終止,才返回結果。可選超時。
name
進程名字
is_alive()
返回進程是否存活
daemon
進程的守護標記,一個布爾值
pid
返回進程ID
exitcode
子進程退出狀態碼
terminate()
終止進程。在unix上使用SIGTERM信號,在windows上使用TerminateProcess()。
Pool()類有如下些方法:
apply(func, args=(), kwds={})
等效內建函數apply()
apply_async(func, args=(), kwds={}, callback=None)
異步,等效內建函數apply()
map(func, iterable, chunksize=None)
等效內建函數map()
map_async(func, iterable, chunksize=None, callback=None)
異步,等效內建函數map()
imap(func, iterable, chunksize=1)
等效內建函數itertools.imap()
imap_unordered(func, iterable, chunksize=1)
像imap()方法,但結果順序是任意的
close()
關閉進程池
terminate()
終止工做進程,垃圾收集鏈接池對象
join()
等待工做進程退出。必須先調用close()或terminate()
Pool.apply_async()和Pool.map_aysnc()又提供瞭如下幾個方法:
get([timeout])
獲取結果對象裏的結果。若是超時沒有,則拋出TimeoutError異常
wait([timeout])
等待可用的結果或超時
ready()
返回調用是否已經完成
successful()
 

博客地址:http://lizhenliang.blog.51cto.com and https://yq.aliyun.com/u/lizhenliang
QQ羣:323779636(Shell/Python運維開發羣)

舉例:
1)簡單的例子,用子進程處理函數
from multiprocessing import Process
import os
 
def worker(name):
    print name
    print 'parent process id:', os.getppid()
    print 'process id:', os.getpid()
 
if __name__ == '__main__':
    p = Process(target=worker, args=('function worker.',))
    p.start()
    p.join()
    print p.name
 
# python test.py
function worker.
parent process id: 9079
process id: 9080
Process-1
Process實例傳入worker函數做爲派生進程執行的任務,用start()方法啓動這個實例。
2)加以說明join()方法
from multiprocessing import Process
import os
 
def worker(n):
    print 'hello world', n
 
if __name__ == '__main__':
    print 'parent process id:', os.getppid()
    for n in range(5):
        p = Process(target=worker, args=(n,))
        p.start()
        p.join()
        print 'child process id:', p.pid
        print 'child process name:', p.name
 
# python test.py
parent process id: 9041
hello world 0
child process id: 9132
child process name: Process-1
hello world 1
child process id: 9133
child process name: Process-2
hello world 2
child process id: 9134
child process name: Process-3
hello world 3
child process id: 9135
child process name: Process-4
hello world 4
child process id: 9136
child process name: Process-5
 
# 把p.join()註釋掉再執行
# python test.py
parent process id: 9041
child process id: 9125
child process name: Process-1
child process id: 9126
child process name: Process-2
child process id: 9127
child process name: Process-3
child process id: 9128
child process name: Process-4
hello world 0
hello world 1
hello world 3
hello world 2
child process id: 9129
child process name: Process-5
hello world 4
能夠看出,在使用join()方法時,輸出的結果都是順序排列的。相反是亂序的。所以join()方法是堵塞父進程,要等待當前子進程執行完後纔會繼續執行下一個子進程。不然會一直生成子進程去執行任務。
在要求輸出的狀況下使用join()可保證每一個結果是完整的。
3)給子進程命名,方便管理
from multiprocessing import Process
import os, time
 
def worker1(n):
    print 'hello world', n
def worker2():
    print 'worker2...'
 
if __name__ == '__main__':
    print 'parent process id:', os.getppid()
    for n in range(3):
        p1 = Process(name='worker1', target=worker1, args=(n,))
        p1.start()
        p1.join()
        print 'child process id:', p1.pid
        print 'child process name:', p1.name
    p2 = Process(name='worker2', target=worker2)
    p2.start()
    p2.join()
    print 'child process id:', p2.pid
    print 'child process name:', p2.name
 
# python test.py
parent process id: 9041
hello world 0
child process id: 9248
child process name: worker1
hello world 1
child process id: 9249
child process name: worker1
hello world 2
child process id: 9250
child process name: worker1
worker2...
child process id: 9251
child process name: worker2
4)設置守護進程,父進程退出也不影響子進程運行
from multiprocessing import Process
 
def worker1(n):
    print 'hello world', n
def worker2():
    print 'worker2...'
 
if __name__ == '__main__':
    for n in range(3):
        p1 = Process(name='worker1', target=worker1, args=(n,))
        p1.daemon = True
        p1.start()
        p1.join()
    p2 = Process(target=worker2)
    p2.daemon = False
    p2.start()
    p2.join()
5)使用進程池
#!/usr/bin/python
# -*- coding: utf-8 -*-
from multiprocessing import Pool, current_process
import os, time, sys
 
def worker(n):
    print 'hello world', n
    print 'process name:', current_process().name  # 獲取當前進程名字
    time.sleep(1)    # 休眠用於執行時有時間查看當前執行的進程
 
if __name__ == '__main__':
    p = Pool(processes=3)
    for i in range(8):
        r = p.apply_async(worker, args=(i,))
        r.get(timeout=5)  # 獲取結果中的數據
    p.close()
 
# python test.py
hello world 0
process name: PoolWorker-1
hello world 1
process name: PoolWorker-2
hello world 2
process name: PoolWorker-3
hello world 3
process name: PoolWorker-1
hello world 4
process name: PoolWorker-2
hello world 5
process name: PoolWorker-3
hello world 6
process name: PoolWorker-1
hello world 7
process name: PoolWorker-2
進程池生成了3個子進程,經過循環執行8次worker函數,進程池會從子進程1開始去處理任務,當到達最大進程時,會繼續從子進程1開始。
在運行此程序同時,再打開一個終端窗口會看到生成的子進程:
# ps -ef |grep python
root      40244   9041  4 16:43 pts/3    00:00:00 python test.py
root      40245  40244  0 16:43 pts/3    00:00:00 python test.py
root      40246  40244  0 16:43 pts/3    00:00:00 python test.py
root      40247  40244  0 16:43 pts/3    00:00:00 python test.py
6)進程池map()方法
map()方法是將序列中的元素經過函數處理返回新列表。
from multiprocessing import Pool
 
def worker(url):
    return 'http://%s' % url
 
urls = ['www.baidu.com', 'www.jd.com']
p = Pool(processes=2)
r = p.map(worker, urls)
p.close()
print r
 
# python test.py
['http://www.baidu.com', 'http://www.jd.com']
7)Queue進程間通訊
multiprocessing支持兩種類型進程間通訊:Queue和Pipe。
Queue庫已經封裝到multiprocessing庫中,在第十章 Python經常使用標準庫已經講解到Queue庫使用,有須要請查看之前博文。
例如:一個子進程向隊列寫數據,一個子進程讀取隊列數據
#!/usr/bin/python
# -*- coding: utf-8 -*-
from multiprocessing import Process, Queue
# 寫數據到隊列
def write(q):
    for n in range(5):
        q.put(n)
        print 'Put %s to queue.' % n
# 從隊列讀數據
def read(q):
    while True:
        if not q.empty():
            value = q.get()
            print 'Get %s from queue.' % value
        else:
            break
if __name__ == '__main__':
    q = Queue()
    pw = Process(target=write, args=(q,))
    pr = Process(target=read, args=(q,))   
    pw.start()
    pw.join()
    pr.start()
    pr.join()
 
# python test.py
Put 0 to queue.
Put 1 to queue.
Put 2 to queue.
Put 3 to queue.
Put 4 to queue.
Get 0 from queue.
Get 1 from queue.
Get 2 from queue.
Get 3 from queue.
Get 4 from queue.
8)Pipe進程間通訊
from multiprocessing import Process, Pipe
 
def f(conn):
    conn.send([42, None, 'hello'])
    conn.close()
 
if __name__ == '__main__':
    parent_conn, child_conn = Pipe()
    p = Process(target=f, args=(child_conn,))
    p.start()
    print parent_conn.recv() 
    p.join()
 
# python test.py
[42, None, 'hello']
Pipe()建立兩個鏈接對象,每一個連接對象都有send()和recv()方法,
9)進程間對象共享
Manager類返回一個管理對象,它控制服務端進程。提供一些共享方式:Value()、Array()、list()、dict()、Event()等
建立Manger對象存放資源,其餘進程經過訪問Manager獲取。
from multiprocessing import Process, Manager
 
def f(v, a, l, d):
    v.value = 100
    a[0] = 123
    l.append('Hello')
    d['a'] = 1
 
mgr = Manager()
v = mgr.Value('v', 0)
a = mgr.Array('d', range(5))
l = mgr.list()
d = mgr.dict()
 
p = Process(target=f, args=(v, a, l, d))
p.start()
p.join()
 
print(v)
print(a)
print(l)
print(d)
 
# python test.py
Value('v', 100)
array('d', [123.0, 1.0, 2.0, 3.0, 4.0])
['Hello']
{'a': 1}
10)寫一個多進程的例子
好比:多進程監控URL是否正常
from multiprocessing import Pool, current_process
import urllib2
 
urls = [
    'http://www.baidu.com',
    'http://www.jd.com',
    'http://www.sina.com',
    'http://www.163.com',
]
 
def status_code(url):
    print 'process name:', current_process().name
    try:
        req = urllib2.urlopen(url, timeout=5)
        return req.getcode()
    except urllib2.URLError:
        return
 
p = Pool(processes=4)
for url in urls:
    r = p.apply_async(status_code, args=(url,))
    if r.get(timeout=5) == 200:
        print "%s OK" %url
    else:
        print "%s NO" %url
 
# python test.py
process name: PoolWorker-1
http://www.baidu.com OK
process name: PoolWorker-2
http://www.jd.com OK
process name: PoolWorker-3
http://www.sina.com OK
process name: PoolWorker-4
http://www.163.com OK
 
15.2 threading
threading模塊相似於multiprocessing多進程模塊,使用方法也基本同樣。threading庫是對thread庫進行二次封裝,咱們主要用到Thread類,用Thread類派生線程對象。
1)使用Thread類實現多線程
from threading import Thread, current_thread
 
def worker(n):
    print 'thread name:', current_thread().name
    print 'hello world', n
    
for n in range(5):
    t = Thread(target=worker, args=(n, ))
    t.start()
    t.join()  # 等待主進程結束
 
# python test.py
thread name: Thread-1
hello world 0
thread name: Thread-2
hello world 1
thread name: Thread-3
hello world 2
thread name: Thread-4
hello world 3
thread name: Thread-5
hello world 4
2)還有一種方式繼承Thread類實現多線程,子類能夠重寫__init__和run()方法實現功能邏輯。
#!/usr/bin/python
# -*- coding: utf-8 -*-
from threading import Thread, current_thread
 
class Test(Thread):
    # 重寫父類構造函數,那麼父類構造函數將不會執行
    def __init__(self, n):
        Thread.__init__(self)
        self.n = n
    def run(self):
        print 'thread name:', current_thread().name
        print 'hello world', self.n
 
if __name__ == '__main__':
    for n in range(5):
        t = Test(n)
        t.start()
        t.join()
 
# python test.py
thread name: Thread-1
hello world 0
thread name: Thread-2
hello world 1
thread name: Thread-3
hello world 2
thread name: Thread-4
hello world 3
thread name: Thread-5
hello world 4
3)Lock
from threading import Thread, Lock, current_thread
 
lock = Lock()
class Test(Thread):
    # 重寫父類構造函數,那麼父類構造函數將不會執行
    def __init__(self, n):
        Thread.__init__(self)
        self.n = n
    def run(self):
        lock.acquire()  # 獲取鎖
        print 'thread name:', current_thread().name
        print 'hello world', self.n
        lock.release()  # 釋放鎖
 
if __name__ == '__main__':
    for n in range(5):
        t = Test(n)
        t.start()
        t.join()
衆所周知,Python多線程有GIL全局鎖,意思是把每一個線程執行代碼時都上了鎖,執行完成後會自動釋放GIL鎖,意味着同一時間只有一個線程在運行代碼。因爲全部線程共享父進程內存、變量、資源,很容易多個線程對其操做,致使內容混亂。
當你在寫多線程程序的時候若是輸出結果是混亂的,這時你應該考慮到在不使用鎖的狀況下,多個線程運行時可能會修改原有的變量,致使輸出不同。
由此看來Python多線程是不能利用多核CPU提升處理性能,但在IO密集狀況下,仍是能提升必定的併發性能。也沒必要擔憂,多核CPU狀況能夠使用多進程實現多核任務。Python多進程是複製父進程資源,互不影響,有各自獨立的GIL鎖,保證數據不會混亂。能用多進程就用吧!

 

第十六章 Python正則表達式

正則表達式在每種語言中都會有,目的就是匹配符合你預期要求的字符串。
Python正則表達式主要由re庫提供,擁有了基本全部的表達式。
16.1 Python正則表達式
符號
描述
示例
.
匹配除換行符(\n)以外的任意單個字符
字符串123\n456,匹配123:1.3
^
匹配字符串開頭
abc\nxyz,匹配以abc開頭的行:^abc
$
匹配字符串結尾
abc\nxyz,匹配以xyz結束的行:xyz$
*
匹配多個
hello\nword,匹配以w開頭d結尾的單詞:w*d
+
匹配1個或多個
abc\nabcc\nadf,匹配abc和abcc:ab+
匹配0個或1個
abc\nac\nadd,匹配abc或ac:a?c
[.]
匹配中括號之中的任意一個字符
abcd\nadd\nbbb,匹配abcd和add:[abc]
[ .-.]
匹配中括號中範圍內的任意一個字符
abcd\nadd\nbbb,匹配abcd和add:[a-c]
[^]
匹配[^字符]以外的任意一個字符
abc\n\abb\nddd,不匹配abc和abb:[^a-c]
{n}或{n,}
匹配花括號前面字符至少n個字符
1\n\12\n123\n1234,匹配123和1234:[0-9]{3}
{n,m}
匹配花括號前面字符至少n個字符,最多m個字符
1\n\12\n123\n1234\n12345,匹配123和1234 :[0-9]{3,4}
|
匹配豎槓兩邊的任意一個
abc\nabd\abe,匹配abc和abd:ab(c|d)
\
轉義符,將特殊符號轉成原有意義
1.2,匹配1.2:1\.2,不然112也會匹配到
 
特殊字符
描述
示例
\A
匹配字符串開始
與^區別是:當使用修飾符re.M匹配多行時,\A將全部字符串做爲一整行處理。
abc123\nabc456,匹配abc123:\Aabc,^則都會匹配到
\Z
匹配字符串結束
與\A同理
\b
匹配字符串開始或結束(邊界)
abc\nabcd,匹配a開頭而且c結尾字符串:\babc\b
\B
與\b相反
 
\d
匹配任意十進制數,等效[0-9]
1\n123\nabc,匹配1和123:[0-9],包含單個數字的都會匹配到,若是隻想匹配1:\b[0-9]\b
\D
匹配任意非數字字符,等效[^0-9]
1\n12\nabc,匹配abc:[^0-9]
\s
匹配任意空白字符,等效[\t\n\r\f\v]
1\n a,注意a前面有個空格,匹配a:\s
\S
匹配任意非空白字符,等效[^\t\n\r\f\v]
1\n a\n ,匹配1和a:\S
\w
匹配任意數字和字母,等效[a-zA-Z0-9_]
1\n a\n ,匹配1和a:\w
\W
與\w相反,等效[^a-zA-Z0-9_]
 
\n
反向引用,n是數字,從1開始編號,表示引用第n個分組匹配的內容
ff,匹配ff:(.)\1,即"ff"
 
擴展正則表達式
描述
( )
匹配小括號中正則表達式或字符。用上面\n特殊字符引用。
(?#...)
註釋小括號內的內容
(?:...)
不保存匹配的分組
(?P<name>...)
命名分組,name是標識名稱,默認是數字ID標識分組匹配
(?=...)
匹配後面能匹配表的達式...,稱爲正先行斷言
(?!...)
匹配後面不能匹配的表達式...,稱爲負先行斷言
(?<=...)
匹配前面能匹配的表達式...,稱爲正後發斷言
(?<!...)
匹配前面不能匹配的表達式...,稱爲負後發斷言
(?(id/name)Y/N)
若是分組提供的id或name存在,則使用Y表達式匹配,不然N表達式匹配
斷言:斷言就是一個條件,判斷某個字符串前面或後面是否知足某種規律的字符串,不能引用。

博客地址:http://lizhenliang.blog.51cto.com and https://yq.aliyun.com/u/lizhenliang
QQ羣:323779636(Shell/Python運維開發羣)


16.2 re庫
re模塊有如下經常使用的方法:
方法
描述
re.compile(pattern, flags=0)
把正則表達式編譯成一個對象
re.findall(pattern, string, flags=0)
以列表形式返回全部匹配的字符串
re.finditer(pattern, string, flags=0)
以迭代器形式返回全部匹配的字符串
re.match(pattern, string, flags=0)
匹配字符串開始,若是不匹配返回None
re.search(pattern, string, flags=0)
掃描字符串尋找匹配,若是符合返回一個匹配對象並終止匹配,不然返回None
re.split(pattern, string, maxsplit=0, flags=0)
以匹配模式做爲分隔符,切分字符串爲列表
re.sub(pattern, repl, string, count=0, flags=0)
字符串替換,repl替換匹配的字符串,repl能夠是一個函數
re.purge()
清除正則表達式緩存
參數說明:
pattern   正則表達式
string    要匹配的字符串
flags     標誌位的修飾符,用於控制表達式匹配模式
標誌位的修飾符,有如下可選項:
修飾符
描述
r e.DEBUG
顯示關於編譯正則的debug信息
re.I/re.IGNORECASE
忽略大小寫
re.L/re.LOCALE
本地化匹配,影響\w,\w,\b,\B,\s和\S
re.M/re.MULTILINE
多行匹配,影響^和$
re.S/re.DOTAIL
匹配全部字符,包括換行符\n,若是沒這個標誌將匹配除了換行符
re.U/re.UNICODE
根據unicode字符集解析字符。影響影響\w,\w,\b,\B,\d,\D,\s和\S
re.X/re.VERBOSE
容許編寫更好看、更可讀的正則表達式,也能夠在表達式添加註釋,下面會講到
 
16.2.1 re.compile()
把正則表達式編譯成一個對象,方便再次調用:
>>> import re
prog = re.compile(pattern)
result = prog.match(string)
等效於
result = re.match(pattern, string)
例如:檢查字符串是否匹配
>>> def displaymatch(match):
...     if match is None:
...         return None
...     return '<Match: %r, group=%r>' % (match.group(), match.groups())
...
>>> valid = re.compile(r"^[a-c1-3]{3}$")
>>> displaymatch(valid.match("a1b"))   # 可用
"<Match: 'a1b', group=()>"
>>> displaymatch(valid.match("a1b2"))  # 不可用
>>> displaymatch(valid.match("bbb"))   # 可用
"<Match: 'bbb', group=()>"
16.2.1 match()
例如:判斷字符串開頭是否匹配字符
>>> m = re.match(r'hello', 'hello world')           
>>> print m  # 匹配到字符串開頭是hello
<_sre.SRE_Match object at 0x7f56d5634030>
>>> m = re.match(r'world', 'hello world')     
>>> print m  # 沒有匹配到
None
正則對象匹配方法:
     1)group([group1, ...])
>>> m = re.match(r'(\w+) (\w+)', 'hello world')
>>> m.group(0)    # 所有組匹配
'hello world'
>>> m.group(1)    # 第一個括號子組
'hello'
>>> m.group(2)    # 第二個括號子組
'world'
>>> m.group(1, 2) # 多個參數返回一個元組
('hello', 'world')
     經過分子重命名的名字來引用分組結果:
>>> m = re.match(r'(?P<first_name>\w+) (?P<last_name>\w+)', 'hello world')     
>>> m.group('first_name')
'hello'
>>> m.group('last_name')
'world'
# 命名組也能夠引用他們的索引
>>> m.group(1)
'hello'
>>> m.group(2)
'world'
     若是一組匹配屢次,只有最後一個匹配:
>>> m = re.match(r"(..)+", "a1b2c3")
>>> m.group(1)
'c3'
     2)groups([default])
     返回一個元組包含全部子組的匹配。
>>> m = re.match(r"(\d+)\.(\d+)", "24.1632")
>>> m.groups()
('24', '1632')
     3)groupdict([default])
     返回子組名字做爲鍵,匹配結果做爲值的字典。
>>> m = re.match(r"(?P<first_name>\w+) (?P<last_name>\w+)", "hello world")
>>> m.groupdict()
{'first_name': 'hello', 'last_name': 'world'}
     4)start()和end()
     例如:去掉郵件地址的某字符
>>> email = "tony@163_126.com"
>>> m = re.search(r"_126", email)
>>> email[:m.start()] + email[m.end():]
'tony@163.com'
5)span()
     以列表形式返回匹配索引開始和結束值:
>>> email = "tony@163_126.com"
>>> m = re.search(r"_126", email)
>>> m.span()
(8, 12)
     6)pos和endpos
     返回字符串開始和結束索引值:
>>> email = "tony@163_126.com"
>>> m = re.search(r"_126", email)
>>> m.pos
0
>>> m.endpos
16
16.2.3 search()
search()方法也具有match()方法的正則對象匹配方法,區別是search()匹配到第一個後就返回並終止匹配。
例如:匹配第一個結果就返回
>>> m = re.search(r"c", "abcdefc")
>>> m.group()
'c'
>>> m.span()
(2, 3)
16.2.4 split()
例如:以數字做爲分隔符拆分字符串
>>> m = re.split(r"\d+", "a1b2c3")       
>>> m
['a', 'b', 'c', '']
16.2.4 sub()
例如:替換2016
>>> m = re.sub(r"\d+", "2017", "the year 2016")
>>> m
'the year 2017'
例如:repl做爲一個函數
>>> def repl(m):                         
...   return str(int(m.group('v')) * 2)     
...
>>> re.sub(r'(?P<v>\d+)', repl, "123abc")
'246abc'
函數返回必須是一個字符串。
16.2.5 findall()和finditer()
例如:獲得全部匹配的數字
>>> text = "a1b2c3"
>>> re.findall(r'\d+', text)
['1', '2', '3']
>>> for m in re.finditer(r'\d+', text):
...   print m.group()
...
1
2
3
16.2.6 原始字符串符號"r"
上面所看到的(r"\d+")其中的r表明原始字符串,沒有它,每一個反斜槓'\'都必須再加一個反斜槓來轉義它。
例如,下面兩行代碼功能上是相同的:
>>> m = re.match(r"\W(.)\1\W", " ff ")
>>> m.group()
' ff '
>>> m = re.match("\\W(.)\\1\\W", " ff ")
>>> m.group()
' ff '
>>> m = re.match("\W(.)\1\W", " ff ")   
>>> m.group()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'NoneType' object has no attribute 'group'
\W匹配第一個和最後一個空字符,(.)匹配第一個f,\1引用前面(.)匹配的結果(仍是f),便是r"ff"
16.3 貪婪和非貪婪匹配
貪婪模式:儘量最多匹配
非貪婪模式,儘量最少匹配,通常在量詞(*、+)後面加個問號就是非貪婪模式。
# 貪婪匹配
>>> re.findall(r"<div>.*</div>", "<div>a</div><div>b</div><div>c</div>")
['<div>a</div><div>b</div><div>c</div>']
# 非貪婪匹配
>>> re.findall(r"<div>.*?</div>", "<div>a</div><div>b</div><div>c</div>")
['<div>a</div>', '<div>b</div>', '<div>c</div>']
>>> re.findall(r"a(\d+)", "a123b")     
['123']
>>> re.findall(r"a(\d+?)", "a123b")
['1']
# 若是右邊有限定,非貪婪失效
>>> re.findall(r"a(\d+)b", "a123b")   
['123']
>>> re.findall(r"a(\d+?)b", "a123b")  
['123']
貪婪匹配是儘量的向右匹配,直到字符串結束。
非貪婪匹配是匹配知足後就結束。
16.3 瞭解擴展表達式
以一個字符串來學習斷言的用法:"A regular expression "
1)(?=...)
正先行斷言,匹配後面能匹配的表達式。
有兩個re字符串,只想匹配regular中的:
>>> re.findall(r"..(?=gular)", "A regular expression") 
['re']
# 再向後匹配幾個字符說明匹配的regular中的。下面都會說明下,再也不註釋
>>> re.findall(r"(?=gular).{5}", "A regular expression")
['gular']
2)(?!...)
負先行斷言,匹配後面不能匹配表達式。
只想匹配expression中的re字符串,排除掉regular單詞:
>>> re.findall(r"re(?!g)", "A regular expression") 
['re']
>>> re.findall(r"re(?!g).{5}", "A regular expression")
['ression']
3)(?<=...)
正向後行斷言,匹配前面能匹配表達式。
只想匹配單詞裏的re,排除開頭的re:
>>> re.findall(r"(?<=\w)re", "A regular expression")
['re']
>>> re.findall(r"(?<=\w)re.", "A regular expression")       
['res']
在re前面有一個或多個字符,因此叫後行斷言,正則匹配是從前向後,當遇到斷言時,會再向字符串前端檢測已掃描的字符,相對於掃描方向是向後的。
4)(?<!...)
負向後行斷言,匹配前面不能匹配的表達式。
只想匹配開頭的re:
>>> re.findall(r"(?<!\w)re", "A regular expression") 
['re']
>>> re.findall(r"(?<!\w)re.", "A regular expression")
['reg']
16.4 修飾符
re.VERBOSE上面說明可能你還不太明白,怎麼個更加可讀呢,這就來看看,下面兩個正則編譯等效:
>>> a = re.compile(r"""\d +  # the integral part
...                    \.    # the decimal point
...                    \d *  # some fractional digits""", re.X)
>>> b = re.compile(r"\d+\.\d*")
當你寫的正則很長的時候,能夠添加註釋。
 
Python正則表達式參考:https://docs.python.org/2/library/re.html

 

第十七章 Python網絡編程

Socket簡介
在網絡上的兩個程序經過一個雙向的通訊鏈接實現數據的交換,這個連接的一端稱爲一個Socket(套接字),用於描述IP地址和端口。
創建網絡通訊鏈接至少要一對端口號(Socket),Socket本質是編程接口(API),對TCP/IP的封裝,提供了網絡通訊能力。
每種服務都打開一個Socket,並綁定到端口上,不一樣的端口對應不一樣的服務,就像http對應80端口。
Socket是面向C/S(客戶端/服務器)模型設計,客戶端在本地隨機申請一個惟一的Socket號,服務器擁有公開的socket,任何客戶端均可以向它發送鏈接請求和信息請求。
好比:用手機打電話給10086客服,你的手機號就是客戶端,10086客服是服務端。必須在知道對方電話號碼前提下才能與對方通信。
Socket數據處理流程如圖:
 
17.1 socket
在Python中提供此服務的模塊是socket和SocketServer,下面是socket經常使用的類、方法:
方法 描述
socket.socket([family[, type[, proto]]]) socket初始化函數,(地址族,socket類型,協議編號)協議編號默認0
socket.AF_INET IPV4協議通訊
socket.AF_INET6 IPV6協議通訊
socket.SOCK_STREAM socket類型,TCP
socket.SOCK_DGRAM socket類型,UDP
socket.SOCK_RAW 原始socket,能夠處理普通socker沒法處理的報文,好比ICMP
socket.SOCK_RDM 更可靠的UDP類型,保證對方收到數據
socket.SOCK_SEQPACKET 可靠的連續數據包服務
socket.socket()對象有如下方法:
accept() 接受鏈接並返回(socket object, address info),address是客戶端地址
bind(address) 綁定socket到本地地址,address是一個雙元素元組(host,port)
listen(backlog) 開始接收鏈接,backlog是最大鏈接數,默認1
connect(address) 鏈接socket到遠程地址
connect_ex(address) 鏈接socket到遠程地址,成功返回0,錯誤返回error值
getpeername() 返回遠程端地址(hostaddr, port)
gettimeout() 返回當前超時的值,單位秒,若是沒有設置返回none
recv(buffersize[, flags]) 接收來自socket的數據,buffersize是接收數據量
send(data[, flags]) 發送數據到socket,返回值是發送的字節數
sendall(data[, flags]) 發送全部數據到socket,成功返回none,失敗拋出異常
setblocking(flag) 設置socket爲阻塞(flag是true)或非阻塞(flag是flase)
溫習下TCP與UDP區別:
TCP和UDP是OSI七層模型中傳輸層提供的協議,提供可靠端到端的傳輸服務。
TCPTransmission Control Protocol,傳輸控制協議),面向鏈接協議,雙方先創建可靠的鏈接,再發送數據。適用於可靠性要求高的應用場景。
UDPUser Data Protocol,用戶數據報協議),面向非鏈接協議,不與對方創建鏈接,直接將數據包發送給對方,所以相對TCP傳輸速度快 。適用於可靠性要求低的應用場景。
17.1.1 TCP編程
下面建立一個服務端TCP協議的Socket演示下。
先寫一個服務端:
#!/usr/bin/python
# -*- coding: utf-8 -*-
import socket
 
HOST = ''                 # 爲空表明全部可用的網卡
PORT = 50007              # 任意非特權端口
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((HOST, PORT))
s.listen(1)   # 最大鏈接數
conn, addr = s.accept()   # 返回客戶端地址
print 'Connected by', addr
while 1:
    data = conn.recv(1024)   # 每次最大接收客戶端發來數據1024字節
    if not data: break       # 當沒有數據就退出死循環 
    print "Received: ", data # 打印接收的數據
    conn.sendall(data)       # 把接收的數據再發給客戶端
conn.close()
再寫一個客戶端:
#!/usr/bin/python
# -*- coding: utf-8 -*-
import socket
 
HOST = '192.168.1.120'    # 遠程主機IP
PORT = 50007              # 遠程主機端口
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))
s.sendall('Hello, world') # 發送數據
data = s.recv(1024)       # 接收服務端發來的數據
s.close()
print 'Received: ', data
寫好後,打開一個終端窗口執行:
# python socket-server.py
監聽中...
# 直到客戶端運行會接收到下面數據並退出
Connected by ('192.168.1.120', 37548)
Received:  Hello, world
再打開一個終端窗口執行:
# 若是端口監據說明服務端運行正常
# netstat -antp |grep 50007
tcp        0      0 0.0.0.0:50007           0.0.0.0:*               LISTEN      72878/python
# python socket-client.py
Received: Hello, world
經過實驗瞭解搭到Socket服務端工做有如下幾個步驟:
1)打開socket
2)綁定到一個地址和端口
3)監聽進來的鏈接
4)接受鏈接
5)處理數據
17.1.2 UDP編程
服務端:
import socket
HOST = ''               
PORT = 50007             
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.bind((HOST, PORT))
while 1:
    data, addr = s.recvfrom(1024)
    print 'Connected by', addr
    print "Received: ", data
    s.sendto("Hello %s"% repr(addr), addr)
conn.close()
客戶端:
import socket
HOST = '192.168.1.99'                 
PORT = 50007             
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.sendto(data, (HOST, PORT))
data = s.recv(1024)
s.close()
print 'Received: ', data
運行方式與TCP編程同樣。
使用UDP協議時,服務端就少了listen()和accept(),不須要創建鏈接就直接接收客戶端的數據,也是把數據直接發送給客戶端。
客戶端少了connect(),一樣直接經過sendto()給服務器發數據。
而TCP協議則前提先創建三次握手。
17.1.3 舉一個更直觀的socket通訊例子
客戶端發送bash命令,服務端接收到並執行,把返回結果迴應給客戶端。
服務端:
#!/usr/bin/python
# -*- coding: utf-8 -*-
import sys
import subprocess
import socket
 
HOST = ''               
PORT = 50007             
try:
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.bind((HOST, PORT))
    s.listen(1)
except socket.error as e:
    s.close()
    print e
    sys.exit(1)
 
while 1:
    conn, addr = s.accept()
    print 'Connected by', addr
    while 1:
        # 每次讀取1024字節
        data = conn.recv(1024)
        if not data: # 客戶端關閉服務端會收到一個空數據
            print repr(addr) + " close."
            conn.close()
            break     
        print "Received: ", data
        cmd = subprocess.Popen(data, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
        result_tuple = cmd.communicate()
        if cmd.returncode != 0 or cmd.returncode == None:
            result = result_tuple[1]
            # result = cmd.stderr.read()
        else:
            result = result_tuple[0]
            # result = cmd.stdout.read()  # 讀不到標準輸出,不知道爲啥,因此不用
        if result:
            conn.sendall(result)
        else:
            conn.sendall("return null")
s.close()
客戶端:
#!/usr/bin/python
# -*- coding: utf-8 -*-
import sys
import socket
 
HOST = '192.168.1.120'   
PORT = 50007             
try:
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect((HOST, PORT))
except socket.error as e:
    s.close()
    print e
    sys.exit(1)
while 1:
    cmd = raw_input("Please input command: ")
    if not cmd: continue
    s.sendall(cmd)
    recv_data = s.recv(1024)
    print 'Received: ', recv_data
s.close()
查看運行效果,先運行服務端,再運行客戶端:
# python socket-server.py
Connected by ('192.168.1.120', 45620)
Received:  ls
Received:  touch a.txt
Received:  ls
 
# python socket-client.py
Please input command: ls
Received: 
socket-client.py
socket-server.py
 
Please input command: touch a.txt
Received:  return null
Please input command: ls
Received: 
a.txt
socket-client.py
socket-server.py
 
Please input command:
我想經過上面這個例子你已經大體掌握了socket的通訊過程。
再舉一個例子,經過socket獲取本機網卡IP:
>>> socket.gethostname()
'ubuntu'
>>> socket.gethostbyname(socket.gethostname())
'127.0.1.1'
 
>>> s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
>>> s.connect(('10.255.255.255', 0))
>>> s.getsockname()
('192.168.1.120', 35765)
>>> s.getsockname()[0]
'192.168.1.120'
 
博客地址:http://lizhenliang.blog.51cto.com and https://yq.aliyun.com/u/lizhenliang
QQ羣:323779636(Shell/Python運維開發羣)

 
17.2 SocketServer
ScoketServer是Socket服務端庫,比socket庫更高級,實現了多線程和多線程,併發處理多個客戶端請求。
下面是幾個經常使用的類:
SocketServer.TCPServer(server_addressRequestHandlerClassbind_and_activate=True) 服務器類,TCP協議
SocketServer.UDPServer(server_addressRequestHandlerClassbind_and_activate=True) 服務器類,UDP協議
SocketServer.BaseServer(server_addressRequestHandlerClass) 這個是全部服務器對象的超類。它定義了接口,不提供大多數方法,在子類中進行。
SocketServer.BaseRequestHandler 這個是全部請求處理對象的超類。它定義了接口,一個具體的請求處理程序子類必須定義一個新的handle()方法。
SocketServer.StreamRequestHandler 流式socket,根據socket生成讀寫socket用的兩個文件對象,調用rfile和wfile讀寫
SocketServer.DatagramRequestHandler 數據報socket,一樣生成rfile和wfile,但UDP不直接關聯socket。這裏rfile是由UDP中讀取的數據生成,wfile則是新建一個StringIO,用於寫數據
SocketServer.ForkingMixIn/ThreadingMixIn 多進程(分叉)/多線程實現異步。混合類,這個類不會直接實例化。用於實現處理多鏈接
SocketServer.BaseServer()對象有如下方法:
fileno() 返回一個整數文件描述符上服務器監聽的套接字
handle_request() 處理一個請求
serve_forever(poll_interval=0.5) 處理,直至有明確要求shutdown()的請求。輪訓關機每poll_interval秒
shutdown() 告訴serve_forever()循環中止並等待
server_close() 清理服務器
address_family 地址族
server_address 監聽的地址
RequestHandlerClass 用戶提供的請求處理類
socket socket對象上的服務器將監聽傳入的請求
allow_reuse_address 服務器是否容許地址的重用。默認False
request_queue_size 請求隊列的大小。
socket_type socket類型。socket.SOCK_STREAM或socket.SOCK_DGRAM
timeout 超時時間,以秒爲單位
finish_request() 實際處理經過實例請求RequestHandleClass並調用其handle()方法
get_request() 必須接受從socket的請求,並返回
handle_error(request, client_address) 若是這個函數被條用handle()
process_request(request, client_address)  
server_activate()  
server_bind() 由服務器構造函數調用的套接字綁定到所需的地址
verify_request(request, client_address) 返回一個布爾值,若是該值是True,則該請求將被處理,若是是False,該請求將被拒絕。
建立一個服務器須要幾個步驟:
1)建立類,繼承請求處理類(BaseRequestHandler),並重載其handle()方法,此方法將處理傳入的請求
2)實例化服務器類之一,它傳遞服務器的地址和請求處理程序類
3)調用handle_request()或serve_forever()服務器對象的方法來處理一個或多個請求
4)調用server_close()關閉套接字
17.2.1 TCP編程
服務端:
#!/usr/bin/python
# -*- coding: utf-8 -*
import SocketServer
 
class MyTCPHandler(SocketServer.BaseRequestHandler):
    """
    請求處理程序類。
    每一個鏈接到服務器都要實例化一次,並且必須覆蓋handle()方法來實現與客戶端通訊
    """
    def handle(self):
        # self.request 接收客戶端數據
        self.data = self.request.recv(1024).strip()
        print "%s wrote:" % (self.client_address[0])
        print self.data
        # 把接收的數據轉爲大寫發給客戶端
        self.request.sendall(self.data.upper())
 
if __name__ == "__main__":
    HOST, PORT = "localhost", 9999
    # 建立服務器並綁定本地地址和端口
    server = SocketServer.TCPServer((HOST, PORT), MyTCPHandler)
    # 激活服務器,會一直運行,直到Ctrl-C中斷
    server.serve_forever()
另外一個請求處理程序類,利用流(類文件對象簡化通訊提供標準文件接口):
class MyTCPHandler(SocketServer.StreamRequestHandler):
    def handle(self):
        # self.rfile建立的是一個類文件對象處理程序,就能夠調用readline()而不是recv()
        self.data = self.rfile.readline().strip()
        print "%s wrote:" % (self.client_address[0])
        print self.data
        # 一樣,self.wfile是一個類文件對象,用於回覆客戶端
        self.wfile.write(self.data.upper())
客戶端:
import socket
import sys
 
HOST, PORT = "localhost", 9999
data = " ".join(sys.argv[1:])
 
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
 
try:
    sock.connect((HOST, PORT))
    sock.sendall(data + "\n")
 
    received = sock.recv(1024)
finally:
    sock.close()
 
print "Sent: %s" % data
print "Received: %s" % received
服務端結果:
# python TCPServer.py
127.0.0.1 wrote:
hello
127.0.0.1 wrote:
nice
客戶端結果:
# python TCPClient.py hello
Sent: hello
Received: HELLO
# python TCPClient.py nice
Sent: nice
Received: NICE
17.2.2 UDP編程
服務端:
import SocketServer
 
class MyTCPHandler(SocketServer.BaseRequestHandler):
    def handle(self):
        self.data = self.request[0].strip()
        self.socket = self.request[1]
        print "%s wrote:" % (self.client_address[0])
        print self.data
        self.socket.sendto(self.data.upper(), self.client_address)
 
if __name__ == "__main__":
    HOST, PORT = "localhost", 9999
    server = SocketServer.UDPServer((HOST, PORT), MyTCPHandler)
    server.serve_forever()
客戶端:
import socket
import sys
 
HOST, PORT = "localhost", 9999
data = " ".join(sys.argv[1:])
 
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
 
sock.sendto(data + "\n", (HOST, PORT))
received = sock.recv(1024)
 
print "Sent: %s" % data
print "Received: %s" % received
與TCP執行結果同樣。
17.2.3 異步混合
建立異步處理,使用ThreadingMixIn和ForkingMixIn類。
ThreadingMixIn類的一個例子:
#!/usr/bin/python
# -*- coding: utf-8 -*
import socket
import threading
import SocketServer
 
class ThreadedTCPRequestHandler(SocketServer.BaseRequestHandler):
 
    def handle(self):
        data = self.request.recv(1024)
        cur_thread = threading.current_thread()
        response = "%s: %s" % (cur_thread.name, data)
        self.request.sendall(response)
 
class ThreadedTCPServer(SocketServer.ThreadingMixIn, SocketServer.TCPServer):
    pass
 
def client(ip, port, message):
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.connect((ip, port))
    try:
        sock.sendall(message)
        response = sock.recv(1024)
        print "Received: %s" % response
    finally:
        sock.close()
 
if __name__ == "__main__":
    # 端口0意味着隨機使用一個未使用的端口
    HOST, PORT = "localhost", 0
 
    server = ThreadedTCPServer((HOST, PORT), ThreadedTCPRequestHandler)
    ip, port = server.server_address
 
    # 服務器啓動一個線程,該線程將開始。每一個線程處理每一個請求
    server_thread = threading.Thread(target=server.serve_forever)
    # 做爲守護線程
    server_thread.daemon = True
    server_thread.start()
    print "Server loop running in thread:", server_thread.name
 
    client(ip, port, "Hello World 1")
    client(ip, port, "Hello World 2")
    client(ip, port, "Hello World 3")
 
    server.shutdown()
    server.server_close()
 
# python socket-server.py
Server loop running in thread: Thread-1
Received: Thread-2: Hello World 1
Received: Thread-3: Hello World 2
Received: Thread-4: Hello World 3

 

第十八章 Python批量管理主機(paramiko、fabric與pexpect)

本章節主要講解運維工程師比較感興趣的知識,那就是運維批量管理,在Python下有 paramiko、fabric和pexpect這三個 模塊可幫助運維實現自動化部署、批量執行命令、文件傳輸等常規任務,接下來一塊兒看看它們的使用方法吧!
18.1 paramiko
paramiko模塊是基於Python實現的SSH遠程安全鏈接,用於SSH遠程執行命令、文件傳輸等功能。
默認Python沒有,須要手動安裝:pip install paramiko
如安裝失敗,能夠嘗試yum安裝:yum install python-paramiko
18.1.1 SSH密碼認證遠程執行命令
#!/usr/bin/python
# -*- coding: utf-8 -*-
import paramiko
import sys
hostname = '192.168.1.215'
port = 22
username = 'root'
password = '123456'
client = paramiko.SSHClient()  # 綁定實例
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
client.connect(hostname, port, username, password, timeout=5)
stdin, stdout, stderr = client.exec_command('df -h')   # 執行bash命令
result = stdout.read()
error = stderr.read()
# 判斷stderr輸出是否爲空,爲空則打印執行結果,不爲空打印報錯信息
if not error:
   print result
else:
   print error
client.close()
18.1.2 私鑰認證遠程執行命令
#!/usr/bin/python
# -*- coding: utf-8 -*-
import paramiko
import sys
hostname = '192.168.1.215'
port = 22
username = 'root'
key_file = '/root/.ssh/id_rsa'
cmd = " ".join(sys.argv[1:])
def ssh_conn(command):
    client = paramiko.SSHClient()
    key = paramiko.RSAKey.from_private_key_file(key_file)
    client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    client.connect(hostname, port, username, pkey=key)
    stdin, stdout, stderr = client.exec_command(command)  # 標準輸入,標準輸出,錯誤輸出
    result = stdout.read()
    error = stderr.read()
    if not error:
        print result
    else:
        print error
    client.close()
if __name__ == "__main__":
    ssh_conn(cmd)
18.1.3 上傳文件到遠程服務器
#!/usr/bin/python
# -*- coding: utf-8 -*-
import os, sys
import paramiko
 
hostname = '192.168.1.215'
port = 22
username = 'root'
password = '123456'
local_path = '/root/test.txt'
remote_path = '/opt/test.txt'
if not os.path.isfile(local_path):
    print local_path + " file not exist!"
    sys.exit(1)
try:
    s = paramiko.Transport((hostname, port))
    s.connect(username = username, password=password)
except Exception as e:
    print e
    sys.exit(1)
sftp = paramiko.SFTPClient.from_transport(s)
# 使用put()方法把本地文件上傳到遠程服務器
sftp.put(local_path, remote_path)       
# 簡單測試是否上傳成功
try:
    # 若是遠程主機有這個文件則返回一個對象,不然拋出異常               
    sftp.file(remote_path) 
    print "上傳成功."
except IOError:
    print "上傳失敗!"
finally:
    s.close()
18.1.4 從遠程服務器下載文件
#!/usr/bin/python
# -*- coding: utf-8 -*-
import os, sys
import paramiko
 
hostname = '192.168.1.215'
port = 22
username = 'root'
password = '123456'
local_path = '/root/test.txt'
remote_path = '/opt/test.txt'
try:
    s = paramiko.Transport((hostname, port))
    s.connect(username=username, password=password)
    sftp = paramiko.SFTPClient.from_transport(s)
except Exception as e:
    print e
    sys.exit(1)
 
try:
    # 判斷遠程服務器是否有這個文件
    sftp.file(remote_path)
    # 使用get()方法從遠程服務器拉去文件
    sftp.get(remote_path, local_path)       
 
except IOError as e:
    print remote_path + "remote file not exist!"
    sys.exit(1)
finally:
    s.close()
 
# 測試是否下載成功
if os.path.isfile(local_path):
    print "下載成功."
else:
    print "下載失敗!"
18.1.5 上傳目錄到遠程服務器
paramiko模塊並無實現直接上傳目錄的類,已經知道了如何上傳文件,再寫一個上傳目錄的代碼就簡單了,利用os庫的os.walk()方法遍歷目錄,再一個個上傳:
#!/usr/bin/python
# -*- coding: utf-8 -*-
import os, sys
import paramiko
 
hostname = '192.168.1.215'
port = 22
username = 'root'
password = '123456'
local_path = '/root/abc'
remote_path = '/opt/abc'
 
# 去除路徑後面正斜槓
if local_path[-1] == '/':
    local_path = local_path[0:-1]
if remote_path[-1] == '/':
    remote_path = remote_path[0:-1]
 
file_list = []
if os.path.isdir(local_path):
    for root, dirs, files in os.walk(local_path):
        for file in files:
            # 獲取文件絕對路徑
            file_path = os.path.join(root, file) 
            file_list.append(file_path)
else:
    print path + "Directory not exist!"
    sys.exit(1)
 
try:
    s = paramiko.Transport((hostname, port))
    s.connect(username=username, password=password)
    sftp = paramiko.SFTPClient.from_transport(s)
except Exception as e:
    print e
 
for local_file in file_list:
    # 替換目標目錄
    remote_file = local_file.replace(local_path, remote_path)
    remote_dir = os.path.dirname(remote_file)
    # 若是遠程服務器沒目標目錄則建立
    try:
        sftp.stat(remote_dir)
    except IOError:
        sftp.mkdir(remote_dir)
    print "%s -> %s" % (local_file, remote_file)
    sftp.put(local_file, remote_file)
s.close()
sftp是安全文件傳輸協議,提供一種安全的加密方法,sftp是SSH的一部分,SFTPClient類實現了sftp客戶端,經過已創建的SSH通道傳輸文件,與其餘的操做,以下:
sftp.getcwd()
返回當前工做目錄
sftp.chdir(path)
改變工做目錄
sftp.chmod(path, mode)
修改權限
sftp.chown(path, uid, gid)
設置屬主屬組
sftp.close()
關閉sftp
sftp.file(filename, mode='r', bufsize=-1)
讀取文件
sftp.from_transport(s)
建立SFTP客戶端通道
sftp.listdir(path='.')
列出目錄,返回一個列表
sftp.listdir_attr(path='.')
列出目錄,返回一個SFTPAttributes列表
sftp.mkdir(path, mode=511)
建立目錄
sftp.normalize(path)
返回規範化path
sftp.open(filename, mode='r', bufsize=-1)
在遠程服務器打開文件
sftp.put(localpath, remotepath, callback=None)
localpath文件上傳到遠程服務器remotepath
sftp.get(remotepath, localpath, callback=None)
從遠程服務器remotepath拉文件到本地localpath
sftp.readlink(path)
返回一個符號連接目標
sftp.remove(path)
刪除文件
sftp.rename(oldpath, newpath)
重命名文件或目錄
sftp.rmdir(path)
刪除目錄
sftp.stat(path)
返回遠程服務器文件信息(返回一個對象的屬性)
sftp.truncate(path, size)
截取文件大小
sftp.symlink(source, dest)
建立一個軟連接(快捷方式)
sftp.unlink(path)
刪除軟連接

博客地址:http://lizhenliang.blog.51cto.com and https://yq.aliyun.com/u/lizhenliang
QQ羣:323779636(Shell/Python運維開發羣)


18.2 fabric
fabric模塊是在paramiko基礎上又作了一層封裝,操做起來更方便。主要用於多臺主機批量執行任務。
默認Python沒有,須要手動安裝:pip install fabric
如安裝失敗,能夠嘗試yum安裝:yum install fabric
Fabric經常使用API:
API類
描述
示例
local
執行本地命令
local('uname -s')
lcd
切換本地目錄
lcd('/opt')
run
執行遠程命令
run('uname -s')
cd
切換遠程目錄
cd('/opt')
sudo
sudo方式執行遠程命令
sudo('/etc/init.d/httpd start')
put
上傳本地文件或目錄到遠程主機
put(remote_path, local_path)
get
從遠程主機下載文件或目錄到本地
put(local_path, remote_path)
open_shell
打開一個shell,相似於SSH鏈接到了遠程主機
open_shell("ifconfig eth0")
prompt
得到用戶輸入信息
prompt('Please input user password: ')
confirm
得到提示信息確認
confirm('Continue[Y/N]?')
reboot
重啓遠程主機
reboot()
@task
函數裝飾器,引用說明函數可調用,不然不可見
 
@runs_once
函數裝飾器,函數只會執行一次
 
當咱們寫好fabric腳本後,須要用fab命令調用執行任務。
命令格式:fab [options] <command>[:arg1,arg2=val2,host=foo,hosts='h1;h2',...] ...
fab命令有如下經常使用選項:
選項
描述
-l
打印可用的命令(函數)
--set=KEY=VALUE,...
逗號分隔,設置環境變量
--shortlist
簡短打印可用命令
-c PATH
指定本地配置文件
-D
不加載用戶known_hosts文件
-f PATH
指定fabfile文件
-g HOST
逗號分隔要操做的主機
-i PATH
指定私鑰文件
-k
不加載來自~/.ssh下的私鑰文件
-p PASSWORD
使用密碼認證and/or sudo
-P
默認爲並行執行方法
--port=PORT
指定SSH鏈接端口
-R ROLES
根據角色操做,逗號分隔
-s SHELL
指定新shell,默認是'/bin/bash -l -c'
--show=LEVELS
以逗號分隔的輸出
--ssh-config-path=PATH
SSH配置文件路徑
-t N
設置鏈接超時時間,單位秒
-T N
設置遠程命令超時時間,單位秒
-u USER
鏈接遠程主機用戶名
-x HOSTS
以逗號分隔排除主機
-z INT
併發進程數
18.2.1 本地執行命令
from fabric.api import local
def command():
    local('ls')
 
# fab command
[localhost] local: ls
fabfile.py  fabfile.pyc  tab.py  tab.pyc
 
Done.
使用fab命令調用,默認尋找當前目錄的fabfile.py文件。
18.2.2 遠程執行命令
from fabric.api import run
def command():
    run('ls')
 
# fab -H 192.168.1.120 -u user command
[192.168.1.120] Executing task 'command'
[192.168.1.120] run: ls
[192.168.1.120] Login password for 'user':
[192.168.1.120] out: access.log  a.py
[192.168.1.120] out:
 
 
Done.
Disconnecting from 192.168.1.120... done.
若是在多臺主機執行,只須要-H後面的IP以逗號分隔便可。
18.2.3 給腳本函數傳入位置參數
from fabric.api import run
 
def hello(name="world"):
    print("Hello %s!" % name)
 
# fab -H localhost hello
[localhost] Executing task 'hello'
Hello world!
 
Done.
# fab -H localhost hello:name=Python
[localhost] Executing task 'hello'
Hello Python!
 
Done.
18.2.4 主機列表組
from fabric.api import run, env
env.hosts = ['root@192.168.1.120:22', 'root@192.168.1.130:22']
env.password = '123.com'
env.exclude_hosts = ['root@192.168.1.120:22']   # 排除主機
def command():
   run('ls')
env做用是定義fabfile全局設定,相似於變量。還有一些經常使用的屬性:
env屬性
描述
示例
env.hosts
定義目標主機
env.hosts = ['192.168.1.120:22']
env.exclude_hosts
排除指定主機
env.exclude_hosts = '[192.168.1.1]'
env.user
定義用戶名
env.user='root'
env.port
定義端口
env.port='22'
env.password
定義密碼
env.password='123'
env.passwords
定義多個密碼,不一樣主機對應不一樣密碼
env.passwords = {'root@192.168.1.120:22': '123'}
env.gateway
定義網關
env.gateway='192.168.1.2'
env.roledefs
定義角色分組
env.roledef = {'web':['192.168.1.11'], 'db':['192.168.1.12']}
env.deploy_release_dir
自定義全局變量,格式:env.+ '變量名'
env.var
18.2.5 定義角色分組
# vi install.py
from fabric.api import run, env
env.roledefs = {
    'web': ['192.168.1.10', '192.168.1.20'],
    'db': ['192.168.1.30', '192.168.1.40']
}
env.password = '123'
@roles('web')
def task1():
   run('yum install httpd -y')
 
@roles('db')
def task2():
   run('yum install mysql-server -y')
 
def deploy():
   execute(task1)
   execute(task2)
# fab -f install.py deploy
18.2.6 上傳目錄到遠程主機
from fabric.api import *
env.hosts = ['192.168.1.120']
env.user = 'user'
env.password = '123.com'
def task():
   put('/root/abc', '/home/user')
   run('ls -l /home/user')
 # fab task              
18.2.7 從遠程主機下載目錄
from fabric.api import *
env.hosts = ['192.168.1.120']
env.user = 'user'
env.password = '123.com'
def task():
   get('/home/user/b', '/opt')
   local('ls -l /opt')
# fab task
18.2.8 打印顏色,有助於關鍵地方醒目
from fabric.colors import *
def show():
   print green('Successful.')
   print red('Failure!')
   print yellow('Warning.')
# fab show
通過上面演示fabric主要相關功能,是否是以爲很適合批量自動部署呢!沒錯,經過編寫簡單的腳本,便可完成複雜的操做。
18.3 pexpect
pexpect是一個用來啓動子程序,並使用正則表達式對程序輸出作出特定響應,以此實現與其自動交互的Python模塊。暫不支持Windows下的Python環境執行。
這裏主要講解run()函數和spawn()類,能完成自動交互,下面簡單瞭解下它們使用。
18.3.1 run()
run()函數用來運行bash命令,相似於os模塊中的system()函數。
參數:run(command, timeout=-1, withexitstatus=False, events=None, extra_args=None, logfile=None, cwd=None, env=None)
例1:執行ls命令
>>> import pexpect
>>> pexpect.run("ls") 
例2:得到命令狀態返回值
>>> command_output, exitstatus = pexpect.run("ls", withexitstatus=1)
command_outout是執行結果,exitstatus是退出狀態值。
18.3.2 spawn()
spawn()是pexpect模塊主要的類,實現啓動子程序,使用pty.fork()生成子進程,並調用exec()系列函數執行命令。
參數:spawn(command, args=[], timeout=30, maxread=2000, searchwindowsize=None, logfile=None, cwd=None, env=None)
spawn()類幾個經常使用函數:
expect(pattern, timeout=-1, searchwindowsize=None)
匹配正則表達式,pattern能夠是正則表達式。
send(s)
給子進程發送一個字符串
sendline(s='')
就像send(),但添加了一個換行符(os.lineseq)
sendcontrol(char)
發送一個控制符,好比ctrl-c、ctrl-d
例子:ftp交互
用ftp命令登陸是這樣的,須要手動輸入用戶名和密碼,才能登陸進去。
# ftp 192.168.1.10
Connected to 192.168.1.10 (192.168.1.10).
220-FileZilla Server version 0.9.46 beta
220-written by Tim Kosse (tim.kosse@filezilla-project.org)
220 Please visit http://sourceforge.net/projects/filezilla/
Name (192.168.1.10:root): yunwei
331 Password required for yunwei
Password:
230 Logged on
Remote system type is UNIX.
ftp>
下面咱們用pexpect幫咱們完成輸入用戶名和密碼:
import pexpect
child = pexpect.spawn('ftp 192.168.1.10')
child.expect('Name .*: ')
child.sendline('yunwei')
child.expect('Password:')
child.sendline('yunweipass')
child.expect('ftp> ')
child.sendline('ls')
child.sendline('bye')
child.expect(pexpect.EOF)   # pexpect.EOF程序打印提示信息
print child.before   # 保存命令執行結果
手動輸入時,是來自鍵盤的標準輸入,而 pexpect是先匹配到關鍵字,再向子進程發送字符串。
pexpect.EOF打印提示信息,child.before保存的是命令執行結果。
經過上面的例子想必你已經知道pexpect主要功能了,在交互場景下頗有用,這裏就講解這麼多了,目的是給你們提供一個自動交互實現思路。
 
小結:
經過對Python下paramiko、fabric和pexpect模塊使用,它們各有本身擅長的一面。
paramiko:方便嵌套系統平臺中,擅長遠程執行命令,文件傳輸。
fabric:方便與shell腳本結合,擅長批量部署,任務管理。
pexpect:擅長自動交互,好比ssh、ftp、telnet。

 

轉自

博客地址:http://lizhenliang.blog.51cto.com and https://yq.aliyun.com/u/lizhenliang

相關文章
相關標籤/搜索