Python

預備知識:
空值是Python裏一個特殊的值,用None表示。None不能理解爲0,因爲0是有意義的,而None是一個特殊的空值。此外,
Python還提供了列表、字典等多種數據類型,還允許創建自定義數據類型。Python的註釋以 # 開頭,後面的文字直到行
尾都算註釋# 這一行全部都是註釋...print 'hello' # 這也是註釋   會輸出hello如果一個字符串包含很多需要轉義
的字符,對每一個字符都進行轉義會很麻煩。爲了避免這種情況,我們可以在字符串前面加個前綴 r ,表示這是一個 raw 字符串,裏面的字符就不需要轉義了。例如:r'\(~_~)/ \(~_~)/'
但是r'...'表示法不能表示多行字符串,也不能表示包含'和 "的字符串(爲什麼?)
如果要表示多行字符串,可以用'''...'''表示:
'''Line 1
Line 2
Line 3'''
上面這個字符串的表示方法和下面的是完全一樣的:
'Line 1\nLine 2\nLine 3'
還可以在多行字符串前面添加 r ,把這個多行字符串也變成一個raw字符串:
r'''Python is created by "Guido".
It is free and easy to learn.
Let's start learn Python in imooc!'''
----------------------------------------------------------------------------------------------------------------------------------------------------------
a = True
print a and 'a=T' or 'a=F'
計算結果不是布爾類型,而是字符串 'a=T',這是爲什麼呢?因爲Python把0、空字符串''和None看成 False,其他數
值和非空字符串都看成 True,所以:True and 'a=T' 計算結果是 'a=T'
繼續計算 'a=T' or 'a=F' 計算結果還是 'a=T'要解釋上述結果,又涉及到 and 和 or 運算的一條重要法則:短路計
算。1. 在計算 a and b 時,如果 a 是 False,則根據與運算法則,整個結果必定爲 False,因此返回 a;如果 a 是 True,則整個計算結果必定取決與 b,因此返回 b。2. 在計算 a or b 時,如果 a 是 True,則根據或運算法則,整個計算結果必定爲 True,因此返回 a;如果 a 是 False,則整個計算結果必定取決於 b,因此返回 b。所以Python解釋器在做布爾運算時,只要能提前確定計算結果,它就不會往後算了,直接返回結果。
Python內置的一種數據類型是列表:list。list是一種有序的集合,可以隨時添加和刪除其中的元素。list是數學意義上的有序集合,也就是說,list中的元素是按照順序排列的。構造list非常簡單,按照上面的代碼,直接用 [ ] 把list的所有元素都括起來,就是一個list對象。通常,我們會把list賦值給一個變量,這樣,就可以通過變量來引用
list:classmates = ['Michael', 'Bob', 'Tracy']
print classmates
由於Python是動態語言,所以list中包含的元素並不要求都必須是同一種數據類型,我們完全可以在list中包含各種:
L = ['Michael', 100, True]
索引從 0 開始,也就是說,第一個元素的索引是0,第二個元素的索引是1,以此類推。我們可以用 -1 這個索引來表示最後一個元素,類似的,倒數第二用 -2 表示,倒數第三用 -3 表示,倒數第四用 -4 表示
append()總是把新的元素添加到 list 的尾部。list的 insert()方法,它接受兩個參數,第一個參數是索引號,第二個參數是待添加的新元素
pop()方法總是刪掉list的最後一個元素,並且它還返回這個元素,所以我們執行 L.pop() 後,會打印出這個元素。可以用 pop(2)把索引爲2的元素刪除
替換: L = ['Adam', 'Lisa', 'Bart']   L[2] = 'Paul'

tuple是另一種有序的列表,中文翻譯爲“ 元組 ”。tuple 和 list 非常類似,但是,tuple一旦創建完畢,就不能修

改了。創建tuple和創建list唯一不同之處是用( )替代了[ ]。獲取 tuple 元素的方式和 list 是一模一樣的
t = (1)  print t結果爲1,因爲()既可以表示tuple,又可以作爲括號表示運算時的優先級,結果 (1) 被Python解釋

器計算出結果 1,導致我們得到的不是tuple,而是整數 1。
正是因爲用()定義單元素的tuple有歧義,所以 Python 規定,單元素 tuple 要多加一個逗號“,”,這樣就避免了歧義:t = (1,)   print t 結果爲(1,)

tuple一開始指向的list並沒有改成別的list,所以,tuple所謂的“不變”是說,tuple的每個元素,指向永遠不變。

即指向'a',就不能改成指向'b',指向一個list,就不能改成指向其他對象,但指向的這個list本身是可變的!
注意: Python代碼的縮進規則。具有相同縮進的代碼被視爲代碼塊,上面的3,4行 print 語句就構成一個代碼塊(但不包括第5行的print)。如果 if 語句判斷爲 True,就會執行這個代碼塊。

縮進請嚴格按照Python的習慣寫法:4個空格,不要使用Tab,更不要混合Tab和空格,否則很容易造成因爲縮進引起的語法錯誤。

注意: if 語句後接表達式,然後用:表示代碼塊開始。

如果你在Python交互環境下敲代碼,還要特別留意縮進,並且退出縮進需要多敲一行回車 練習:
score=55
if score >= 60:
    score=bytes(score)
    print 'passed:'+score
else:
    print 'nopass'
age = 8
if age >= 6:
    print 'teenager'
elif age >= 18:
    print 'adult'
else:
    print 'kid'
Python的 for 循環就可以依次把list或tuple的每個元素迭代出來:L = ['Adam', 'Lisa', 'Bart']
for name in L:
    print name
注意:  name 這個變量是在 for 循環中定義的,意思是,依次取出list中的每一個元素,並把元素賦值給 name,然後執行for循環體,這樣,遍歷一個list或tuple就非常容易了
花括號 {} 表示這是一個dict,然後按照 key: value, 寫出來即可。最後一個 key: value 的逗號可以省略。由於

dict也是集合,len() 函數可以計算任意集合的大小:

可以簡單地使用 d[key] 的形式來查找對應的 value
通過 key 訪問 dict 的value,如果key不存在,會直接報錯:KeyError。要避免 KeyError 發生,有兩個辦法:一是先判斷一下 key 是否存在,用 in 操作符:

if 'Paul' in d:
    print d['Paul']
二是使用dict本身提供的一個 get 方法,在Key不存在的時候,返回None
dict的第一個特點是查找速度快,無論dict有10個元素還是10萬個元素,查找速度都一樣。而list的查找速度隨着元素增加而逐漸下降。

不過dict的查找速度快不是沒有代價的,dict的缺點是佔用內存大,還會浪費很多內容,list正好相反,佔用內存小,但是查找速度慢。由於dict是按 key 查找,所以,在一個dict中,key不能重複。

dict的第二個特點就是存儲的key-value序對是沒有順序的!這和list不一樣
dict的第三個特點是作爲 key 的元素必須不可變,Python的基本類型如字符串、整數、浮點數都是不可變的,都可以作爲 key。但是list是可變的,就不能作爲 key。
d = {
    'Adam': 95,
    'Lisa': 85,
    'Bart': 59
}要把新同學'Paul'的成績 72 加進去,用賦值語句 d['Paul'] = 72如果 key 已經存在,則賦值會用新的 value 替

換掉原來的 value
練習:
d = {
    'Adam': 95,
    'Lisa': 85,
    'Bart': 59
}
for key in d:
    print key
    print d[key]
    print d.get(key)
set 持有一系列元素,這一點和 list 很像,但是set的元素沒有重複,而且是無序的,這點和 dict 的 key很像。創建 set 的方式是調用 set() 並傳入一個 list,list的元素將作爲set的元素:s = set(['A', 'B', 'C'])添加重複元素,重複元素最後只存在一個
由於set存儲的是無序集合,所以我們沒法通過索引來訪問。

訪問 set中的某個元素實際上就是判斷一個元素是否在set中。
s = set(['Adam', 'Lisa', 'Bart', 'Paul'])
Bart是該班的同學嗎?'Bart' in s輸出True
遍歷:
s = set([('Adam', 95), ('Lisa', 85), ('Bart', 59)])
for name in s:
    print name
輸出('Lisa', 85)
('Adam', 95)
('Bart', 59)
添加元素時,用set的add()方法,刪除set中的元素時,用set的remove()方法,不存在會報錯

在Python中,定義一個函數要使用 def 語句,依次寫出函數名、括號、括號中的參數和冒號:,然後,在縮進塊中編寫函數體,函數的返回值用 return 語句返回。return None可以簡寫爲return。
在語法上,返回一個tuple可以省略括號,而多個變量可以同時接收一個tuple,按位置賦給對應的值,所以,Python的函數返回多值其實就是返回一個tuple
使用遞歸函數需要注意防止棧溢出。在計算機中,函數調用是通過棧(stack)這種數據結構實現的,每當進入一個函數調用,棧就會加一層棧幀,每當函數返回,棧就會減一層棧幀。由於棧的大小不是無限的,所以,遞歸調用的次數過多,會導致棧溢出。
Python自帶的 int() 函數,其實就有兩個參數,int()函數的第二個參數是轉換進制,如果不傳,默認是十進制 

(base=10),如果傳了,就用傳入的參數。
可變參數的名字前面有個 * 號,我們可以傳入0個、1個或多個參數給可變參數

在python2中有input和raw_input兩個函數,其中raw_input將所有輸入作爲字符串看待,返回字符串類型。
input函數支持表達式、數字類型、字符串類型,接受爲表達式時,只返回其執行結果。
在python3中對input和raw_input函數進行了整合,僅保留了input函數(認爲raw_input函數是冗餘的)。
同時改變了input的用法——將所有的輸入按照字符串進行處理,並返回一個字符串。
 sqrt()方法返回x的平方根(x>0)。
語法
以下是sqrt()方法的語法:
import math

math.sqrt( x )
 
注意:此函數是無法直接訪問的,所以我們需要導入math模塊,然後需要用math的靜態對象來調用這個函數。參數
1.    x -- 這是一個數值表達式。返回值
此方法返回x的平方根,對於x>0。例子下面的例子顯示了sqrt()方法的使用。

#!/usr/bin/python
import math  # This will import math module
print "math.sqrt(100) : ", math.sqrt(100)
print "math.sqrt(7) : ", math.sqrt(7)
print "math.sqrt(math.pi) : ", math.sqrt(math.pi)

當我們運行上面的程序,它會產生以下結果:
math.sqrt(100) : 10.0
math.sqrt(7) : 2.64575131106
math.sqrt(math.pi) : 1.77245385091
map()是 Python 內置的高階函數,它接收一個函數 f 和一個 list,並通過把函數 f 依次作用在 list 的每個元素上

,得到一個新的 list 並返回。注意:map()函數不改變原有的 list,而是返回一個新的 list。

在使用Python編譯的時候出現如下錯誤:
SyntaxError: Non-ASCII character '\xe5' in file Test1.py on line 8, but no encoding declared; see 

http://www.python.org/peps/pep-0263.html for details
解決方法
python的默認編碼文件是用的ASCII碼,將文件存成了UTF-8,編譯就可以通過。或在在py文件開頭(必須是第一行)加入
[python] view plain copy
#coding=utf-8  
或者
[python] view plain copy
# -*- coding:utf-8 -*-  
原因
如果要在python2的py文件裏面寫中文,則必須要添加一行聲明文件編碼的註釋,否則python2會默認使用ASCII編碼。
可以使用unicode函數
print u'你好';
print (unicode("請輸入銷售額", encoding="utf-8"))
將utf-8編碼轉換爲unicode就可以輸出中文了。
將ASCII字符轉換爲對應的數值即‘a’-->65,使用ord函數,ord('a')
反正,使用chr函數,將數值轉換爲對應的ASCII字符,chr(65)

reduce()函數也是Python內置的一個高階函數。reduce()函數接收的參數和 map()類似,一個函數 f,一個list,但行爲和 map()不同,reduce()傳入的函數 f 必須接收兩個參數,reduce()對list的每個元素反覆調用函數f,並返回最終結果值。reduce()還可以接收第3個可選參數,作爲計算的初始值。
例如,編寫一個f函數,接收x和y,返回x和y的和:

def f(x, y):
    return x + y
調用 reduce(f, [1, 3, 5, 7, 9])時,reduce函數將做如下計算:

先計算頭兩個元素:f(1, 3),結果爲4;
再把結果和第3個元素計算:f(4, 5),結果爲9;
再把結果和第4個元素計算:f(9, 7),結果爲16;
再把結果和第5個元素計算:f(16, 9),結果爲25;
由於沒有更多的元素了,計算結束,返回結果25。
filter()函數接收一個函數 f 和一個list,這個函數 f 的作用是對每個元素進行判斷,返回 True或 False,filter

()根據判斷結果自動過濾掉不符合條件的元素,返回由符合條件元素組成的新list。
注意: s.strip(rm) 刪除 s 字符串中開頭、結尾處的 rm 序列的字符。
當rm爲空時,默認刪除空白符(包括'\n', '\r', '\t', ' ')
Python內置的 sorted()函數可對list進行升序排序
但 sorted()也是一個高階函數,它可以接收一個比較函數來實現自定義排序,比較函數的定義是,傳入兩個待比較的元
素 x, y,如果 x 應該排在 y 的前面,返回 -1,如果 x 應該排在 y 的後面,返回 1。如果 x 和 y 相等,返回 0。
定義一個函數 f(),我們讓它返回一個函數 g,可以這樣寫:

def f():
    print 'call f()...'
    # 定義函數g:
    def g():
        print 'call g()...'
    # 返回函數g:
    return g
調用函數 f,我們會得到 f 返回的一個函數:

>>> x = f()   # 調用f()
call f()...
>>> x   # 變量x是f()返回的函數:
<function g at 0x1037bf320>
>>> x()   # x指向函數,因此可以調用
call g()...   # 調用x()就是執行g()函數定義的代碼
內層函數引用了外層函數的變量(參數也算變量),然後返回內層函數的情況,稱爲閉包(Closure)。

閉包的特點是返回的函數還引用了外層函數的局部變量,所以,要正確使用閉包,就要確保引用的局部變量在函數返回後不能變。舉例如下:
# 希望一次返回3個函數,分別計算1x1,2x2,3x3:
def count():
    fs = []
    for i in range(1, 4):
        def f():
             return i*i
        fs.append(f)
    return fs

f1, f2, f3 = count()
你可能認爲調用f1(),f2()和f3()結果應該是1,4,9,但實際結果全部都是 9(請自己動手驗證)。
原因就是當count()函數返回了3個函數時,這3個函數所引用的變量 i 的值已經變成了3。由於f1、f2、f3並沒有被調用,所以,此時他們並未計算 i*i,當 f1 被調用時:

>>> f1()
9     # 因爲f1現在才計算i*i,但現在i的值已經變爲3
因此,返回函數不要引用任何循環變量,或者後續會發生變化的變量。
def count():
    fs = []
    for i in range(1, 4):
        def f(j):
            def g():
                return j*j
            return g
        r = f(i)
        fs.append(r)
    return fs
f1, f2, f3 = count()
print f1(), f2(), f3()
輸出1 4 9

關鍵字lambda 表示匿名函數,冒號前面的 x 表示函數參數。
匿名函數有個限制,就是只能有一個表達式,不寫return,返回值就是該表達式的結果。
Python的 decorator 本質上就是一個高階函數,它接收一個函數作爲參數,然後,返回一個新函數。
使用 decorator 用Python提供的 @ 語法,這樣可以避免手動編寫 f = decorate(f) 這樣的代碼。要讓 @log 自適應任何參數定義的函數,可以利用Python的 *args 和 **kw,保證任意個數的參數總是能正常調用。

在定義 Person 類時,可以爲Person類添加一個特殊的__init__()方法,當創建實例時,__init__()方法被自動調用,我們就能在此爲每個實例都統一加上以下屬性:

class Person(object):
    def __init__(self, name, gender, birth):
        self.name = name
        self.gender = gender
        self.birth = birth
__init__() 方法的第一個參數必須是 self(也可以用別的名字,但建議使用習慣用法),後續參數則可以自由指定,和定義函數沒有任何區別。

相應地,創建實例時,就必須要提供除 self 以外的參數:

xiaoming = Person('Xiao Ming', 'Male', '1991-1-1')
xiaohong = Person('Xiao Hong', 'Female', '1992-2-2')
只有以雙下劃線開頭的"__job"不能直接被外部訪問。
但是,如果一個屬性以"__xxx__"的形式定義,那它又可以被外部訪問了,以"__xxx__"定義的屬性在Python的類中被稱
爲特殊屬性,有很多預定義的特殊屬性可以使用,通常我們不要把普通屬性用"__xxx__"定義。
以單下劃線開頭的屬性"_xxx"雖然也可以被外部訪問,但是,按照習慣,他們不應該被外部訪問。
在class中定義的全部是實例方法,實例方法第一個參數 self 是實例本身。
要在class中定義類方法,需要這麼寫:
class Person(object):
    count = 0
    @classmethod
    def how_many(cls):
        return cls.count
    def __init__(self, name):
        self.name = name
        Person.count = Person.count + 1
print Person.how_many()
p1 = Person('Bob')
print Person.how_many()
通過標記一個 @classmethod,該方法將綁定到 Person 類上,而非類的實例。類方法的第一個參數將傳入類本身,通
常將參數名命名爲 cls,上面的 cls.count 實際上相當於 Person.count。
因爲是在類上調用,而非實例上調用,因此類方法無法獲得任何實例變量,只能獲得類的引用。
如果已經定義了Person類,需要定義新的Student和Teacher類時,可以直接從Person類繼承:
class Person(object):
    def __init__(self, name, gender):
        self.name = name
        self.gender = gender
定義Student類時,只需要把額外的屬性加上,例如score:
class Student(Person):
    def __init__(self, name, gender, score):
        super(Student, self).__init__(name, gender)
        self.score = score
一定要用 super(Student, self).__init__(name, gender) 去初始化父類,否則,繼承自 Person 的 Student 將沒有 name 和 gender。
函數super(Student, self)將返回當前類繼承的父類,即 Person ,然後調用__init__()方法,注意self參數已在
super()中傳入,在__init__()中將隱式傳遞,不需要寫出(也不能寫)。
當我們拿到變量 p、s、t 時,可以使用 isinstance 判斷類型:
>>> isinstance(p, Person)
True    # p是Person類型
>>> isinstance(p, Student)
False   # p不是Student類型
>>> isinstance(p, Teacher)
False   # p不是Teacher類型
對於實例變量,dir()返回所有實例屬性,包括`__class__`這類有特殊意義的屬性。注意到方法`whoAmI`也是 s 的一個屬性。
如何去掉`__xxx__`這類的特殊屬性,只保留我們自己定義的屬性?回顧一下filter()函數的用法。
dir()返回的屬性是字符串列表,如果已知一個屬性名稱,要獲取或者設置對象的屬性,就需要用 getattr() 和 setattr( )函數了:
>>> getattr(s, 'name')  # 獲取name屬性
'Bob'
>>> setattr(s, 'name', 'Adam')  # 設置新的name屬性
>>> s.name
'Adam'
>>> getattr(s, 'age')  # 獲取age屬性,但是屬性不存在,報錯:
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'Student' object has no attribute 'age'
>>> getattr(s, 'age', 20)  # 獲取age屬性,如果屬性不存在,就返回默認值20:
20
如果要把一個類的實例變成 str,就需要實現特殊方法__str__(),Python 定義了__str__()和__repr__()兩種方法,
__str__()用於顯示給用戶,而__repr__()用於顯示給開發人員。
有一個偷懶的定義__repr__的方法:
class Person(object):
    def __init__(self, name, gender):
        self.name = name
        self.gender = gender
    def __str__(self):
        return '(Person: %s, %s)' % (self.name, self.gender)
    __repr__ = __str__
要讓 len() 函數工作正常,類必須提供一個特殊方法__len__(),它返回元素的個數。例如,我們寫一個 Students 類,把名字傳進去:
class Students(object):
    def __init__(self, *args):
        self.names = args
    def __len__(self):
        return len(self.names)
對 int、str 等內置數據類型排序時,Python的 sorted() 按照默認的比較函數 cmp 排序,但是,如果對一組 
Student 類的實例排序時,就必須提供我們自己的特殊方法 __cmp__():
class Student(object):
    def __init__(self, name, score):
        self.name = name
        self.score = score
    def __str__(self):
        return '(%s: %s)' % (self.name, self.score)
    __repr__ = __str__
    def __cmp__(self, s):
        if self.name < s.name:
            return -1
        elif self.name > s.name:
            return 1
        else:
            return 0
上述 Student 類實現了__cmp__()方法,__cmp__用實例自身self和傳入的實例 s 進行比較,如果 self 應該排在前
面,就返回 -1,如果 s 應該排在前面,就返回1,如果兩者相當,返回 0。所有的函數都是可調用對象。
一個類實例也可以變成一個可調用對象,只需要實現一個特殊方法__call__()。
我們把 Person 類變成一個可調用對象:
class Person(object):
    def __init__(self, name, gender):
        self.name = name
        self.gender = gender
    def __call__(self, friend):
        print 'My name is %s...' % self.name
        print 'My friend is %s...' % friend
現在可以對 Person 實例直接調用:
>>> p = Person('Bob', 'male')
>>> p('Tim')
My name is Bob...
My friend is Tim...


Python進階(二十一)-Python學習進階資料
  學習Python已經將近2周了,掌握了基本的語法,並學習了簡單的爬蟲操作,現將相關學習資料整理如下。大部分資料均取材於慕課網,感覺一路學下來受益匪淺。

1.Python入門
2.Python進階
3.Python裝飾器
4.Python-面向對象
5.Python-走進Requests庫
6.Python操作MySQL數據庫
7.Python遇見數據採集
8.Python開發簡單爬蟲
9. 多線程爬蟲
10. Django初體檢
11.Django入門與實戰
12.Flask 開發基礎與入門
13.在 Flask 應用中使用 MySQL
14.Flask 與 BootStrap 搭建網頁
  當然,這其中也不乏收費的教學視頻。不過,Maybe可以在別的地方找到該資源~(竊喜)

15. Python分佈式爬蟲打造搜索引擎 Scrapy精講
16. 強力django+殺手級xadmin 打造上線標準的在線教育平臺

  Demo


Python進階(一)-初識Python數據元素:列表&元組
Python不像JS或者PHP這種弱類型語言裏在字符串連接時會自動轉換類型,而是直接報錯。要解決這個方法只有提前把int轉成string,然後再拼接字符串即可。

如代碼:

# coding=utf8

str='你的分數是:'

num=82

text=str+num+'分 | 瓊臺博客'

printtext
執行結果

直接報錯:TypeError: cannot concatenate 'str' and 'int' objects

解決這個方法只有提前把num轉換爲字符串類型,可以使用bytes函數把int型轉換爲string型。

代碼:

# coding=utf8

str='你的分數是:'

num=82

num=bytes(num)

text=str+num+'分 | 瓊臺博客'

printtext
結果搞定:


  畢業論文已完成,下面就是等待盲審結果了。在此期間,已感覺論文無從看起。就學習一下Python吧,聽說這是一門很神奇的語言。下面言歸正傳~ 
  在線文檔查詢:點擊查看 
  IDE:IntelJ。 
  有關IntelJ的安裝與註冊詳見博文《IntelliJ IDEA 2016註冊方法和註冊碼》。 
  下面介紹一下Python中幾種不熟悉的數據元素:列表、元組、字典、時間。

1列表
1.1初始化列表
list=['physics', 'chemistry', 1997, 2000];
1.2訪問列表中的值
list[0]
1.3更新列表
nums[0]="ljq";
1.4刪除列表元素
del nums[0];
1.5列表腳本操作符
  列表對+和的操作符與字符串相似。+號用於組合列表,號用於重複列表,例如:

print len([1, 2, 3]); #3
print [1, 2, 3] + [4, 5, 6]; #[1, 2, 3, 4, 5, 6]
print ['Hi!'] * 4; #['Hi!', 'Hi!', 'Hi!', 'Hi!']
print 3 in [1, 2, 3] #True
for x in [1, 2, 3]: print x, #1 2 3
1.6列表截取
L=['spam', 'Spam', 'SPAM!'];
print L[2]; #'SPAM!'
print L[-2]; #'Spam'
print L[1:]; #['Spam', 'SPAM!']
1.7列表函數&方法
list.append(obj) #在列表末尾添加新的對象
list.count(obj) #統計某個元素在列表中出現的次數
list.extend(seq) #在列表末尾一次性追加另一個序列中的多個值(用新列表擴展原來的列表)
list.index(obj) #從列表中找出某個值第一個匹配項的索引位置,索引從0開始
list.insert(index, obj) #將對象插入列表
list.pop(obj=list[-1]) #移除列表中的一個元素(默認最後一個元素),並且返回該元素的值
list.remove(obj) #移除列表中某個值的第一個匹配項
list.reverse() #反向列表中元素,倒轉
list.sort([func]) #對原列表進行排序
2元組(tuple)
  Python的元組與列表類似,不同之處在於元組的元素不能修改;元組使用小括號(),列表使用方括號[];元組創建很簡單,只需要在括號中添加元素,並使用逗號(,)隔開即可,

tup1 = ('physics', 'chemistry', 1997, 2000);
 創建空元組,例如:

tup = ();
  元組中只有一個元素時,需要在元素後面添加逗號,例如:

tup1 = (50,);
  元組與字符串類似,下標索引從0開始,可以進行截取,組合等。

2.1訪問元組
tup1 = ('physics', 'chemistry', 1997, 2000);
tup1[0]#physics
2.2修改元組
  元組中的元素值是不允許修改的,但我們可以對元組進行連接組合,例如:

tup1 = (12, 34.56);
tup2 = ('abc', 'xyz');
 #以下修改元組元素操作是非法的。
 tup1[0] = 100;
 #創建一個新的元組
 tup3 = tup1 + tup2;
 print tup3; #(12, 34.56, 'abc', 'xyz')
2.3刪除元組
  元組中的元素值是不允許刪除的,可以使用del語句來刪除整個元組,例如:

tup = ('physics', 'chemistry', 1997, 2000);
print tup;
del tup;
2.4元組運算符
  與字符串一樣,元組之間可以使用+號和*號進行運算。這就意味着他們可以組合和複製,運算後會生成一個新的元組。 


2.5元組索引&截取
L = ('spam', 'Spam', 'SPAM!');
print L[2]; #'SPAM!'
print L[-2]; #'Spam'
print L[1:]; #['Spam', 'SPAM!']
2.6元組內置函數
cmp(tuple1, tuple2) 比較兩個元組元素。
len(tuple) 計算元組元素個數。
max(tuple) 返回元組中元素最大值。
min(tuple) 返回元組中元素最小值。
tuple(seq) 將列表轉換爲元組。

Python進階(二)-初識Python數據元素:字典&時間
3字典
3.1字典簡介
  字典(dic dictionary)是除列表之外python中最靈活的內置數據結構類型。 列表是有序的對象結合,字典是無序的對象集合。兩者之間的區別在於:字典當中的元素是通過鍵來存取的,而不是通過偏移存取。 
  字典由鍵和對應的值組成。字典也被稱作關聯數組或哈希表。基本語法如下:

dict = {'Alice': '2341', 'Beth': '9102', 'Cecil': '3258'};
  也可如此創建字典:

dict1 = { 'abc': 456 };
dict2 = { 'abc': 123, 98.6: 37 };
  每個鍵與值必須用冒號隔開(:),每對用逗號分割,整體放在花括號中({})。鍵必須獨一無二,但值則不必;值可以取任何數據類型,但必須是不可變的,如字符串,數或元組。

3.2訪問字典裏的值
dict = {'name': 'Zara', 'age': 7, 'class': 'First'};
print "dict['name']: ", dict['name'];
print "dict['age']: ", dict['age'];
3.3修改字典
  向字典添加新內容的方法是增加新的鍵/值對,修改或刪除已有鍵/值對。 
如下實例:

dict = {'name': 'Zara', 'age': 7, 'class': 'First'};
dict["age"]=27; #修改已有鍵的值
dict["school"]="wutong"; #增加新的鍵/值對
print "dict['age']: ", dict['age'];
print "dict['school']: ", dict['school'];
3.4刪除字典
del dict['name']; # 刪除鍵是'name'的條目
dict.clear(); # 清空詞典所有條目
del dict ; # 刪除詞典
注意:字典不存在,del會引發一個異常
3.5字典內置函數&方法
cmp(dict1, dict2) #比較兩個字典元素。
len(dict) #計算字典元素個數,即鍵的總數。
str(dict) #輸出字典可打印的字符串表示。
type(variable) #返回輸入的變量類型,如果變量是字典就返回字典類型。
clear() #刪除字典內所有元素
copy() #返回一個字典的淺複製
fromkeys() #創建一個新字典,以序列seq中元素做字典的鍵,val爲字典所有鍵對應的初始值
get(key, default=None) #返回指定鍵的值,如果值不在字典中返回default值
has_key(key) #如果鍵在字典dict裏返回true,否則返回false
items() #以列表返回可遍歷的(鍵, 值) 元組數組
keys() #以列表返回一個字典所有的鍵
setdefault(key, default=None) #和get()類似, 但如果鍵不存在於字典中,將會添加鍵並將值設爲default
update(dict2) #把字典dict2的鍵/值對更新到dict裏
values() #以列表返回字典中的所有值
4日期和時間
4.1獲取當前時間
import time, datetime;
localtime = time.localtime(time.time())
print "Local current time :", localtime
  說明:time.struct_time(tm_year=2014, tm_mon=3, tm_mday=21, tm_hour=15, tm_min=13, tm_sec=56, tm_wday=4, tm_yday=80, tm_isdst=0)屬於struct_time元組,struct_time元組具有如下屬性: 


4.2獲取格式化的時間
  可以根據需求選取各種格式,但是最簡單的獲取可讀的時間模式的函數是asctime():

4.2.1日期轉換爲字符串
  首選:print time.strftime(‘%Y-%m-%d %H:%M:%S’); 
  其次:print datetime.datetime.strftime(datetime.datetime.now(), ‘%Y-%m-%d %H:%M:%S’) 
  最後:print str(datetime.datetime.now())[:19]

4.2.2字符串轉換爲日期
expire_time = "2013-05-21 09:50:35"
d = datetime.datetime.strptime(expire_time,"%Y-%m-%d %H:%M:%S")
print d;

4.2.3獲取日期差
oneday = datetime.timedelta(days=1)
#今天,2014-03-21
today = datetime.date.today()
#昨天,2014-03-20
yesterday = datetime.date.today() - oneday
#明天,2014-03-22
tomorrow = datetime.date.today() + oneday
#獲取今天零點的時間,2014-03-21 00:00:00
today_zero_time=datetime.datetime.strftime(today, '%Y-%m-%d %H:%M:%S')
#0:00:00.001000 
print datetime.timedelta(milliseconds=1), #1毫秒
#0:00:01 
print datetime.timedelta(seconds=1), #1秒
#0:01:00 
print datetime.timedelta(minutes=1), #1分鐘
#1:00:00 
print datetime.timedelta(hours=1), #1小時
#1 day, 0:00:00 
print datetime.timedelta(days=1), #1天
#7 days, 0:00:00
print datetime.timedelta(weeks=1)

4.2.4獲取時間差
#1 day, 0:00:00
oneday = datetime.timedelta(days=1)
#今天,2014-03-21 16:07:23.943000
today_time = datetime.datetime.now()
#昨天,2014-03-20 16:07:23.943000
yesterday_time = datetime.datetime.now() - oneday
#明天,2014-03-22 16:07:23.943000
tomorrow_time = datetime.datetime.now() + oneday

  注意時間是浮點數,帶毫秒。 
  那麼要獲取當前時間,需要格式化一下:

print datetime.datetime.strftime(today_time, '%Y-%m-%d %H:%M:%S')
print datetime.datetime.strftime(yesterday_time, '%Y-%m-%d %H:%M:%S')
print datetime.datetime.strftime(tomorrow_time, '%Y-%m-%d %H:%M:%S')

4.2.5獲取上個月最後一天
last_month_last_day = datetime.date(datetime.date.today().year,datetime.date.today().month,1)-datetime.timedelta(1)
1
4.2.6字符串日期格式化爲秒數
  返回浮點類型

expire_time = "2013-05-21 09:50:35"
d = datetime.datetime.strptime(expire_time,"%Y-%m-%d %H:%M:%S")
time_sec_float = time.mktime(d.timetuple())
print time_sec_float

4.2.7日期格式化爲秒數
  返回浮點類型

d = datetime.date.today()
time_sec_float = time.mktime(d.timetuple())
print time_sec_float

4.2.8秒數轉字符串
time_sec = time.time()
print time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(time_sec))


Python進階(三)-函數式編程之reduce()
  官方解釋如下: 
  Apply function of two arguments cumulatively to the items of sequence, from left to right, so as to reduce the sequence to a single value. For example, reduce(lambda x, y: x+y, [1, 2, 3, 4, 5]) calculates ((((1+2)+3)+4)+5). The left argument, x, is the accumulated value and the right argument, y, is the update value from the sequence. If the optional initializer is present, it is placed before the items of the sequence in the calculation, and serves as a default when the sequence is empty. If initializer is not given and sequence contains only one item, the first item is returned. 
  格式: reduce (func, seq[, init()]) 
  reduce()函數即爲化簡函數,它的執行過程爲:每一次迭代,都將上一次的迭代結果(注:第一次爲init元素,如果沒有指定init則爲seq的第一個元素)與下一個元素一同傳入二元func函數中去執行。在reduce()函數中,init是可選的,如果指定,則作爲第一次迭代的第一個元素使用,如果沒有指定,就取seq中的第一個元素。

舉例
  有一個序列集合,例如[1,1,2,3,2,3,3,5,6,7,7,6,5,5,5],統計這個集合所有鍵的重複個數,例如1出現了兩次,2出現了兩次等。大致的思路就是用字典存儲,元素就是字典的key,出現的次數就是字典的value。方法依然很多 
第一種:for循環判斷

def statistics(lst): 
  dic = {} 
  for k in lst: 
    if not k in dic: 
      dic[k] = 1
    else: 
      dic[k] +=1
  return dic
lst = [1,1,2,3,2,3,3,5,6,7,7,6,5,5,5] 
print(statistics(lst)) 

第二種:比較取巧的,先把列表用set方式去重,然後用列表的count方法

def statistics2(lst): 
  m = set(lst) 
  dic = {} 
  for x in m: 
    dic[x] = lst.count(x) 
  return dic
lst = [1,1,2,3,2,3,3,5,6,7,7,6,5,5,5] 
print statistics2(lst) 

第三種:用reduce方式

def statistics(dic,k):
  if not k in dic:
    dic[k] = 1
  else:
    dic[k] +=1
  return dic
lst = [1,1,2,3,2,3,3,5,6,7,7,6,5,5,5]
print reduce(statistics,lst,{}) 
#提供第三個參數,第一次,初始字典爲空,作爲statistics的第一個參數,然後遍歷lst,作爲第二個參數,然後將返回的字典集合作爲下一次的第一個參數
或者 
d = {} 
d.extend(lst) 
print reduce(statistics,d) 
#不提供第三個參數,但是要在保證集合的第一個元素是一個字典對象,作爲statistics的第一個參數,遍歷集合依次作爲第二個參數 

  通過上面的例子發現,凡是要對一個集合進行操作的,並且要有一個統計結果的,能夠用循環或者遞歸方式解決的問題,一般情況下都可以用reduce方式實現。


Python進階(四)-淺談Python閉包
  在函數內部定義的函數和外部定義的函數是一樣的,只是他們無法被外部訪問:

def g():
    print 'g()...'
def f():
    print 'f()...'
    return g

  將 g 的定義移入函數 f 內部,防止其他代碼調用 g:

def f():
    print 'f()...'
    def g():
        print 'g()...'
    return g

  但是,考察定義的 calc_sum 函數:

def calc_sum(lst):
    def lazy_sum():
        return sum(lst)
    return lazy_sum

  注意: 發現沒法把 lazy_sum 移到 calc_sum 的外部,因爲它引用了 calc_sum 的參數 lst。

像這種內層函數引用了外層函數的變量(參數也算變量),然後返回內層函數的情況,稱爲閉包(Closure)。
  閉包的特點是返回的函數還引用了外層函數的局部變量,所以,要正確使用閉包,就要確保引用的局部變量在函數返回後不能變。舉例如下:

# 希望一次返回3個函數,分別計算1x1,2x2,3x3:
def count():
    fs = [ ]
    for i in range(1, 4):
        def f():
             return i*i
        fs.append(f)
    return fs
f1, f2, f3 = count()

  你可能認爲調用f1(),f2()和f3()結果應該是1,4,9,但實際結果全部都是 9(請自己動手驗證)。 
  原因就是當count()函數返回了3個函數時,這3個函數所引用的變量 i 的值已經變成了3。由於f1、f2、f3並沒有被調用,所以,此時他們並未計算 i*i,當 f1 被調用時:

>>> f1()
9     # 因爲f1現在才計算i*i,但現在i的值已經變爲3

  因此,返回函數不要引用任何循環變量,或者後續會發生變化的變量。

舉例
  返回閉包不能引用循環變量,請改寫count()函數,讓它正確返回能計算1x1、2x2、3x3的函數。 
  考察下面的函數 f:

def f(j):
    def g():
        return j*j
    return g

  它可以正確地返回一個閉包g,g所引用的變量j不是循環變量,因此將正常執行。 
  在count函數的循環內部,如果藉助f函數,就可以避免引用循環變量i。 
  參考代碼:

def count():
    fs = []
    for i in range(1, 4):
        def f(j):
            def g():
                return j*j
            return g
        r = f(i)
        fs.append(r)
    return fs
f1, f2, f3 = count()
print f1(), f2(), f3()

程序實例


Python進階(五)-淺談python匿名函數
  高階函數可以接收函數做參數,有些時候,我們不需要顯式地定義函數,直接傳入匿名函數更方便。 
  在Python中,對匿名函數提供了有限支持。還是以map()函數爲例,計算 f(x)=x2 時,除了定義一個f(x)的函數外,還可以直接傳入匿名函數:

>>> map(lambda x: x * x, [1, 2, 3, 4, 5, 6, 7, 8, 9])
[1, 4, 9, 16, 25, 36, 49, 64, 81]

  通過對比可以看出,匿名函數 lambda x: x * x 實際上就是:

def f(x):
    return x * x

關鍵字lambda 表示匿名函數,冒號前面的 x 表示函數參數。
  匿名函數有個限制,就是只能有一個表達式,不寫return,返回值就是該表達式的結果。 
  使用匿名函數,可以不必定義函數名,直接創建一個函數對象,很多時候可以簡化代碼:

>>> sorted([1, 3, 9, 5, 0], lambda x,y: -cmp(x,y))
[9, 5, 3, 1, 0]

  返回函數的時候,也可以返回匿名函數:

>>> myabs = lambda x: -x if x < 0 else x 
>>> myabs(-1)

>>> myabs(1)

舉例
  利用匿名函數簡化以下代碼:

def is_not_empty(s):
    return s and len(s.strip()) > 0
filter(is_not_empty, ['test', None, '', 'str', '  ', 'END'])

  定義匿名函數時,沒有return關鍵字,且表達式的值就是函數返回值。 
參考代碼:

print filter(lambda s: s and len(s.strip())>0, ['test', None, '', 'str', '  ', 'END'])

Python進階(六)-python編寫無參數decorator
  Python的 decorator 本質上就是一個高階函數,它接收一個函數作爲參數,然後,返回一個新函數。 
  使用 decorator 用Python提供的 @ 語法,這樣可以避免手動編寫 f = decorate(f) 這樣的代碼。 
  考察一個@log的定義:

def log(f):
    def fn(x):
        print 'call ' + f.__name__ + '()...'
        return f(x)
    return fn

  對於階乘函數,@log工作得很好:

@log
def factorial(n):
    return reduce(lambda x,y: x*y, range(1, n+1))
print factorial(10)

  結果: 
  call factorial()… 
  3628800 
  但是,對於參數不是一個的函數,調用將報錯:

@log
def add(x, y):
    return x + y
print add(1, 2)

  結果: 
  Traceback (most recent call last): 
   File “test.py”, line 15, in 
   print add(1,2) 
  TypeError: fn() takes exactly 1 argument (2 given) 
  因爲 add() 函數需要傳入兩個參數,但是 @log 寫死了只含一個參數的返回函數。 
  要讓 @log 自適應任何參數定義的函數,可以利用Python的 *args 和 **kw,保證任意個數的參數總是能正常調用:

def log(f):
    def fn(*args, **kw):
        print 'call ' + f.__name__ + '()...'
        return f(*args, **kw)
    return fn

  現在,對於任意函數,@log 都能正常工作。

舉例
  請編寫一個@performance,它可以打印出函數調用的時間。 
  計算函數調用的時間可以記錄調用前後的當前時間戳,然後計算兩個時間戳的差。 
  參考代碼:

import time
def performance(f):
    def fn(*args, **kw):
        t1 = time.time()
        r = f(*args, **kw)
        t2 = time.time()
        print 'call %s() in %fs' % (f.__name__, (t2 - t1))
        return r
    return fn
 
@performance
def factorial(n):
    return reduce(lambda x,y: x*y, range(1, n+1))
print factorial(10)

Python進階(七)-淺談python3和Python2的區別
  不管使用何種版本的Python語言,都需要遵循編程規範,不該有的縮進一定不能有。否則會出現莫名其妙的錯誤,如下圖所示: 

  在應用Python編程時,首先要明確所使用Python版本,因爲版本2與3之間存在着很多細節性的差異。稍有不慎就會入坑~下面介紹一下具體應用中的細節性差異。 
  具體參照:https://docs.python.org/3/whatsnew/3.0.html

print函數
  Python3中print爲一個函數,必須用括號括起來;Python2中print爲class)。Python 2 的 print 聲明已經被 print() 函數取代了,這意味着我們必須包裝我們想打印在小括號中的對象。

舉例
在Python 2中:

print 'Hello, World!'

在Python 3中:

print( 'Hello, World!')

reduce()函數
  在Python 3裏,reduce()函數已經被從全局名字空間裏移除了,它現在被放置在fucntools模塊裏。使用時要先引入from functools import reduce

try except
try: 
except Exception, e :

    try:
         except    Exception as e :

打開文件
原: file( ….. ) 
或 open(…..) 
改爲: 
只能用 open(…..)

從鍵盤錄入一個字符串
原: raw_input( “提示信息” ) 
改爲: input( “提示信息” )

整形除法自動轉爲float
python2: 
1/2 
2/2 
1//2 



python3: 
1/2 
2/2 
1//2 
0.5 
1.0 
0

新的字符串格式化方法format取代%5.
xrange重命名爲range.
!=取代 < >
long重命名爲int.
exec變成函數
Py3.X源碼文件默認使用utf-8編碼,這就使得以下代碼是合法的:
    >>> 中國 = 'china' 
    >>>print(中國) 
    china 
python3中替換python2中cmp函數
  Python 3.4.3 的版本中已經沒有cmp函數,被operator模塊代替,在交互模式下使用時,需要導入模塊。在沒有導入模塊情況下,會出現下面的錯誤: 
 
  提示找不到cmp函數了,那麼在python3中該如何使用這個函數呢? 
  所以要導入模塊 
 
看下面給的內置函數

operator.lt(a, b)     #相當於 a<b 從第一個數字或字母(ASCII)比大小
operator.le(a, b)     #相當於a<=b
operator.eq(a, b)     #相當於a==b 字母完全一樣,返回True
operator.ne(a, b)     #相當於a!=b
operator.ge(a, b)     #相當於 a>=b
operator.gt(a, b)     #相當於a>b
operator.__lt__(a, b)     
operator.__le__(a, b)     
operator.__eq__(a, b)     
operator.__ne__(a, b)     
operator.__ge__(a, b)     
operator.__gt__(a, b)

  這幾個函數就是用來替換之前的cmp,函數的返回值是布爾值。

除法
在python3.0中,/ 現在總是執行真除法,不管操作數的類型,都會返回包含任何餘數的一個浮點結果。// 執行Floor除法,它截除掉餘數並且針對整數操作數返回一個整數,如果有任何一個操作數是浮點數類型,則返回一個浮點數。
在python2.6中,/ 表示傳統除法,如果兩個操作數都是整數的話,執行截斷的整數除法(好像是Floor除法????);否則,執行浮點除法(保留餘數,好像是真除法?)。//執行Floor除法,並且像在python3.0中一樣工作,對於整數執行截斷除法,對於浮點數執行浮點除法。

Python進階(八)-編寫帶參數decorator
  繼續考察@log 裝飾器:

def log(f):
    def fn(x):
        print 'call ' + f.__name__ + '()...'
        return f(x)
    return fn

  發現對於被裝飾的函數,log打印的語句是不能變的(除了函數名)。 
  如果有的函數非常重要,希望打印出’[INFO] call xxx()…’,有的函數不太重要,希望打印出’[DEBUG] call xxx()…’,這時,log函數本身就需要傳入’INFO’或’DEBUG’這樣的參數,類似這樣:

@log('DEBUG')
def my_func():
    pass

  把上面的定義翻譯成高階函數的調用,就是:

my_func = log('DEBUG')(my_func)

  上面的語句看上去還是比較繞,再展開一下:

log_decorator = log('DEBUG')
my_func = log_decorator(my_func)

  上面的語句又相當於:

log_decorator = log('DEBUG')
@log_decorator
def my_func():
    pass

  所以,帶參數的log函數首先返回一個decorator函數,再讓這個decorator函數接收my_func並返回新函數:

def log(prefix):
    def log_decorator(f):
        def wrapper(*args, **kw):
            print '[%s] %s()...' % (prefix, f.__name__)
            return f(*args, **kw)
        return wrapper
    return log_decorator
 
@log('DEBUG')
def test():
    pass
print test()

執行結果: 
[DEBUG] test()… 
None 
  對於這種3層嵌套的decorator定義,你可以先把它拆開:

# 標準decorator:
def log_decorator(f):
    def wrapper(*args, **kw):
        print '[%s] %s()...' % (prefix, f.__name__)
        return f(*args, **kw)
    return wrapper
return log_decorator
 
# 返回decorator:
def log(prefix):
    return log_decorator(f)


  拆開以後會發現,調用會失敗,因爲在3層嵌套的decorator定義中,最內層的wrapper引用了最外層的參數prefix,所以,把一個閉包拆成普通的函數調用會比較困難。不支持閉包的編程語言要實現同樣的功能就需要更多的代碼。

舉例
  在@performance實現打印秒的同時,請給 @performace 增加一個參數,允許傳入’s’或’ms’:

@performance('ms')
def factorial(n):
    return reduce(lambda x,y: x*y, range(1, n+1))

  要實現帶參數的@performance,就需要實現:

my_func = performance('ms')(my_func)

需要3層嵌套的decorator來實現。 
參考代碼:

import time
def performance(unit):
    def perf_decorator(f):
        def wrapper(*args, **kw):
            t1 = time.time()
            r = f(*args, **kw)
            t2 = time.time()
            t = (t2 - t1) * 1000 if unit=='ms' else (t2 - t1)
            print 'call %s() in %f %s' % (f.__name__, t, unit)
            return r
        return wrapper
    return perf_decorator
 
@performance('ms')
def factorial(n):
    return reduce(lambda x,y: x*y, range(1, n+1))
print factorial(10)

Python進階(九)-Python陷阱:Nonetype
  今天解決一位網友的問題,內容如下: 
  請教代碼問題

def calc_prod(lst):
    def ff():
        print map(lambda x:x*x,lst)
    return ff
 
f = calc_prod([1, 2, 3, 4])
print f()

結果: 
[1, 4, 9, 16] 
None 
  這樣寫輸出爲什麼有None 
  針對這一問題,自己在IDEA中進行了調試,發現果然多輸出了一行None。出現這一現象確實很令人費解。 
自己寫了簡單的測試語句,如下:

b = print(5)
print(b)

通過斷點調試,內容如下: 
 
  發現b的值確實爲None,且其類型爲NoneType。

那麼什麼是Nonetype?
  要理解這個,首先要理解Python對象,python對象具有三個特性:身份、類型、值。 
  這三個特性在對象創建時被賦值。只有值可以改變,其他只讀。類型本身也是對象。 
  Null與None是Python的特殊類型,Null對象或者是None Type,它只有一個值None. 
  它不支持任何運算也沒有任何內建方法. None和任何其他的數據類型比較永遠返回False。 
  None有自己的數據類型NoneType。你可以將None複製給任何變量,但是你不能創建其他NoneType對象。 
  一句話總結:Null對象是python對象,又叫做NoneType,None是這個對象的值。 
  看過了NoneType的解釋,之所以出現None就很好理解了。 
  NoneType之所以出現是因爲定義了一個變量或函數,卻沒有值或者返回值,因此會默認值爲None。 
  而在上面的程序中,雖然高階函數calc_prod()有返回值ff,但是ff()卻沒有返回值,則傳遞到外層函數calc_prod()同樣沒有返回值,故輸出了None。 若代碼改爲如下所示,則可以完美實現列表的輸出。

def calc_prod(lst):
    def ff():
        return map(lambda x:x*x,lst)
    return ff
 
    f = calc_prod([1, 2, 3, 4])
    print(f())

Python進階(十)-Python 編程規範
  在學習一門新的編程語言時,掌握其良好的編程規範可避免一些細節性錯誤的發生。去除一些不必要的學習障礙。

分號
  不要在行尾加分號, 也不要用分號將兩條命令放在同一行. 
行長度 
  每行不超過80個字符 
例外:

1.長的導入模塊語句
2.註釋裏的URL
  不要使用反斜槓連接行. 
  Python會將圓括號, 中括號和花括號中的行隱式連接起來 , 你可以利用這個特點. 如果需要, 你可以在表達式外圍增加一對額外的圓括號.

foo_bar(self, width, height, color='black', design=None, x='foo',emphasis=None, highlight=0)
     if (width == 0 and height == 0 and
         color == 'red' and emphasis == 'strong'):

  如果一個文本字符串在一行放不下, 可以使用圓括號來實現隱式行連接:

x = ('This will build a very long long '
     'long long long long long long string')

  在註釋中,如果必要,將長的URL放在一行上。 
Yes:

# See details at
# http://www.example.com/us/developer/documentation/api/content/v2.0/csv_file_name_extension_full_specification.html

No:

# See details at
# http://www.example.com/us/developer/documentation/api/content/\
# v2.0/csv_file_name_extension_full_specification.html

注意上面例子中的元素縮進; 你可以在本文的縮進部分找到解釋.

括號
  寧缺毋濫的使用括號 
  除非是用於實現行連接, 否則不要在返回語句或條件語句中使用括號. 不過在元組兩邊使用括號是可以的. 
Yes:

 if foo:
         bar()
     while x:
         x = bar()
     if x and y:
         bar()
     if not x:
         bar()
     return foo
     for (x, y) in dict.items(): ...

No:

 if (x):
         bar()
     if not(x):
         bar()
     return (foo)

縮進
  用4個空格來縮進代碼 
  絕對不要用tab, 也不要tab和空格混用. 對於行連接的情況, 你應該要麼垂直對齊換行的元素(見行長度部分的示例), 或者使用4空格的懸掛式縮進(這時第一行不應該有參數): 
Yes:

       # Aligned with opening delimiter
       foo = long_function_name(var_one, var_two,
                                var_three, var_four)
 
       # Aligned with opening delimiter in a dictionary
       foo = {
           long_dictionary_key: value1 +
                                value2,
           ...
       }
 
       # 4-space hanging indent; nothing on first line
       foo = long_function_name(
           var_one, var_two, var_three,
           var_four)
 
       # 4-space hanging indent in a dictionary
       foo = {
           long_dictionary_key:
               long_dictionary_value,
           ...
       }

No:

      # Stuff on first line forbidden
      foo = long_function_name(var_one, var_two,
          var_three, var_four)
 
      # 2-space hanging indent forbidden
      foo = long_function_name(
        var_one, var_two, var_three,
        var_four)
 
      # No hanging indent in a dictionary
      foo = {
          long_dictionary_key:
              long_dictionary_value,
              ...
      }

空行
  頂級定義之間空兩行, 方法定義之間空一行 
  頂級定義之間空兩行, 比如函數或者類定義. 方法定義, 類定義與第一個方法之間, 都應該空一行. 函數或方法中, 某些地方要是你覺得合適, 就空一行.

空格
  按照標準的排版規範來使用標點兩邊的空格。 
  括號內不要有空格. 
Yes:

spam(ham[1], {eggs: 2}, [])

No:

spam( ham[ 1 ], { eggs: 2 }, [ ] )

  不要在逗號, 分號, 冒號前面加空格, 但應該在它們後面加(除了在行尾). 
Yes:

if x == 4:
         print x, y
     x, y = y, x
     ```
No:  

if x == 4 : 
print x , y 
x , y = y , x

參數列表, 索引或切片的左括號前不應加空格.
Yes: spam(1)
no: spam (1)
Yes: dict['key'] = list[index]
No:  dict ['key'] = list [index]
在二元操作符兩邊都加上一個空格, 比如賦值(=), 比較(==, <, >, !=, <>, <=, >=, in, not in, is, is not), 布爾(and, or, not). 至於算術操作符兩邊的空格該如何使用, 需要你自己好好判斷. 不過兩側務必要保持一致.
Yes: x == 1
No:  x<1
當’=’用於指示關鍵字參數或默認參數值時, 不要在其兩側使用空格.
Yes: def complex(real, imag=0.0): return magic(r=real, i=imag)
No:  def complex(real, imag = 0.0): return magic(r = real, i = imag)
不要用空格來垂直對齊多行間的標記, 因爲這會成爲維護的負擔(適用於:, #, =等):
Yes:

 foo = 1000  # comment
 long_name = 2  # comment that should not be aligned
 dictionary = {
     "foo": 1,
     "long_name": 2,
     }
No:

 foo        = 1000  # comment
 long_name  = 2     # comment that should not be aligned
 dictionary = {
     "foo"      : 1,
     "long_name": 2,
     }
Shebang
  大部分.py文件不必以#!作爲文件的開始. 根據 PEP-394 , 程序的main文件應該以 #!/usr/bin/python2或者 #!/usr/bin/python3開始. 
  (譯者注: 在計算機科學中, Shebang (也稱爲Hashbang)是一個由井號和歎號構成的字符串行(#!), 其出現在文本文件的第一行的前兩個字符. 在文件中存在Shebang的情況下, 類Unix操作系統的程序載入器會分析Shebang後的內容, 將這些內容作爲解釋器指令, 並調用該指令, 並將載有Shebang的文件路徑作爲該解釋器的參數. 例如, 以指令#!/bin/sh開頭的文件在執行時會實際調用/bin/sh程序.) 
  #!先用於幫助內核找到Python解釋器, 但是在導入模塊時, 將會被忽略. 因此只有被直接執行的文件中才有必要加入#!.

註釋
  確保對模塊, 函數, 方法和行內註釋使用正確的風格

文檔字符串
  Python有一種獨一無二的的註釋方式: 使用文檔字符串. 文檔字符串是包, 模塊, 類或函數裏的第一個語句. 這些字符串可以通過對象的doc成員被自動提取, 並且被pydoc所用. (你可以在你的模塊上運行pydoc試一把, 看看它長什麼樣). 我們對文檔字符串的慣例是使用三重雙引號”“”( PEP-257 ). 一個文檔字符串應該這樣組織: 首先是一行以句號, 問號或驚歎號結尾的概述(或者該文檔字符串單純只有一行). 接着是一個空行. 接着是文檔字符串剩下的部分, 它應該與文檔字符串的第一行的第一個引號對齊. 下面有更多文檔字符串的格式化規範.

模塊
  每個文件應該包含一個許可樣板. 根據項目使用的許可(例如, Apache 2.0, BSD, LGPL, GPL), 選擇合適的樣板. 
函數和方法 
下文所指的函數,包括函數, 方法, 以及生成器. 
一個函數必須要有文檔字符串, 除非它滿足以下條件: 
外部不可見 
非常短小 
簡單明瞭 
  文檔字符串應該包含函數做什麼, 以及輸入和輸出的詳細描述. 通常, 不應該描述”怎麼做”, 除非是一些複雜的算法. 文檔字符串應該提供足夠的信息, 當別人編寫代碼調用該函數時, 他不需要看一行代碼, 只要看文檔字符串就可以了. 對於複雜的代碼, 在代碼旁邊加註釋會比使用文檔字符串更有意義. 
  關於函數的幾個方面應該在特定的小節中進行描述記錄, 這幾個方面如下文所述. 每節應該以一個標題行開始. 標題行以冒號結尾. 除標題行外, 節的其他內容應被縮進2個空格. 
Args: 
  列出每個參數的名字, 並在名字後使用一個冒號和一個空格, 分隔對該參數的描述.如果描述太長超過了單行80字符,使用2或者4個空格的懸掛縮進(與文件其他部分保持一致). 描述應該包括所需的類型和含義. 如果一個函數接受*foo(可變長度參數列表)或者**bar (任意關鍵字參數), 應該詳細列出*foo和**bar. 
Returns: (或者 Yields: 用於生成器) 
  描述返回值的類型和語義. 如果函數返回None, 這一部分可以省略. 
Raises: 
列出與接口有關的所有異常.

def fetch_bigtable_rows(big_table, keys, other_silly_variable=None):
    """Fetches rows from a Bigtable.
    Retrieves rows pertaining to the given keys from the Table instance
    represented by big_table.  Silly things may happen if
    other_silly_variable is not None.
    Args:
        big_table: An open Bigtable Table instance.
        keys: A sequence of strings representing the key of each table row
            to fetch.
        other_silly_variable: Another optional variable, that has a much
            longer name than the other args, and which does nothing.
    Returns:
        A dict mapping keys to the corresponding table row data
        fetched. Each row is represented as a tuple of strings. For
        example:
        {'Serak': ('Rigel VII', 'Preparer'),
         'Zim': ('Irk', 'Invader'),
         'Lrrr': ('Omicron Persei 8', 'Emperor')}
        If a key from the keys argument is missing from the dictionary,
        then that row was not found in the table.
    Raises:
        IOError: An error occurred accessing the bigtable.Table object.
    """
    pass


  類應該在其定義下有一個用於描述該類的文檔字符串. 如果你的類有公共屬性(Attributes), 那麼文檔中應該有一個屬性(Attributes)段. 並且應該遵守和函數參數相同的格式.

class SampleClass(object):
    """Summary of class here.
    Longer class information....
    Longer class information....
    Attributes:
        likes_spam: A boolean indicating if we like SPAM or not.
        eggs: An integer count of the eggs we have laid.
    """
 
    def __init__(self, likes_spam=False):
        """Inits SampleClass with blah."""
        self.likes_spam = likes_spam
        self.eggs = 0
 
    def public_method(self):
        """Performs operation blah."""

塊註釋和行註釋
  最需要寫註釋的是代碼中那些技巧性的部分. 如果你在下次代碼審查的時候必須解釋一下, 那麼你應該現在就給它寫註釋. 對於複雜的操作, 應該在其操作開始前寫上若干行註釋. 對於不是一目瞭然的代碼, 應在其行尾添加註釋.

‘# We use a weighted dictionary search to find out where i is in 
‘# the array. We extrapolate position based on the largest num 
‘# in the array and the array size and then do binary search to 
‘# get the exact number.’

if i & (i-1) == 0: # true iff i is a power of 2

  爲了提高可讀性, 註釋應該至少離開代碼2個空格. 
  另一方面, 絕不要描述代碼. 假設閱讀代碼的人比你更懂Python, 他只是不知道你的代碼要做什麼.

‘# BAD COMMENT: Now go through the b array and make sure whenever i occurs 
‘# the next element is i+1


  如果一個類不繼承自其它類, 就顯式的從object繼承. 嵌套類也一樣. 
Yes: class SampleClass(object): 
pass 
class OuterClass(object): 
class InnerClass(object): 
pass 
class ChildClass(ParentClass): 
“”“Explicitly inherits from another class already.”“” 
No: class SampleClass: 
pass 
class OuterClass: 
class InnerClass: 
pass

  繼承自 object 是爲了使屬性(properties)正常工作, 並且這樣可以保護你的代碼, 使其不受Python 3的一個特殊的潛在不兼容性影響. 這樣做也定義了一些特殊的方法, 這些方法實現了對象的默認語義, 包括 new, init, delattr, getattribute, setattr, hash, repr, and str .

字符串
  即使參數都是字符串, 使用%操作符或者格式化方法格式化字符串. 不過也不能一概而論, 你需要在+和%之間好好判定. 
Yes: x = a + b 
x = ‘%s, %s!’ % (imperative, expletive) 
x = ‘{}, {}!’.format(imperative, expletive) 
x = ‘name: %s; score: %d’ % (name, n) 
x = ‘name: {}; score: {}’.format(name, n) 
No: x = ‘%s%s’ % (a, b) # use + in this case 
x = ‘{}{}’.format(a, b) # use + in this case 
x = imperative + ‘, ’ + expletive + ‘!’ 
x = ‘name: ’ + name + ‘; score: ’ + str(n) 
  避免在循環中用+和+=操作符來累加字符串. 由於字符串是不可變的, 這樣做會創建不必要的臨時對象, 並且導致二次方而不是線性的運行時間. 作爲替代方案, 你可以將每個子串加入列表, 然後在循環結束後用 .join 連接列表. (也可以將每個子串寫入一個 cStringIO.StringIO 緩存中.) 
Yes: items = [‘

‘] 
for last_name, first_name in employee_list: 
items.append(‘’ % (last_name, first_name)) 
items.append(‘
%s, %s
‘) 
employee_table = ”.join(items) 
No: employee_table = ‘’ 
for last_name, first_name in employee_list: 
employee_table += ‘’ % (last_name, first_name) 
employee_table += ‘
%s, %s
’ 
  在同一個文件中, 保持使用字符串引號的一致性. 使用單引號’或者雙引號”之一用以引用字符串, 並在同一文件中沿用. 在字符串內可以使用另外一種引號, 以避免在字符串中使用. GPyLint已經加入了這一檢查.
(譯者注:GPyLint疑爲筆誤, 應爲PyLint.) 
Yes: 
Python(‘Why are you hiding your eyes?’) 
Gollum(“I’m scared of lint errors.”) 
Narrator(‘“Good!” thought a happy Python reviewer.’) 
No: 
Python(“Why are you hiding your eyes?”) 
Gollum(‘The lint. It burns. It burns us.’) 
Gollum(“Always the great lint. Watching. Watching.”) 
  爲多行字符串使用三重雙引號”“”而非三重單引號’‘’. 當且僅當項目中使用單引號’來引用字符串時, 纔可能會使用三重’‘’爲非文檔字符串的多行字符串來標識引用. 文檔字符串必須使用三重雙引號”“”. 不過要注意, 通常用隱式行連接更清晰, 因爲多行字符串與程序其他部分的縮進方式不一致. 
Yes: 
print (“This is much nicer.\n” 
“Do it this way.\n”) 
No: 
print “”“This is pretty ugly. 
Don’t do this. 
“”“

文件和sockets
  在文件和sockets結束時, 顯式的關閉它. 
  除文件外, sockets或其他類似文件的對象在沒有必要的情況下打開, 會有許多副作用, 例如:

1.它們可能會消耗有限的系統資源, 如文件描述符. 如果這些資源在使用後沒有及時歸還系統, 那麼用於處理這些對象的代碼會將資源消耗殆盡.
2.持有文件將會阻止對於文件的其他諸如移動、刪除之類的操作.
3.僅僅是從邏輯上關閉文件和sockets, 那麼它們仍然可能會被其共享的程序在無意中進行讀或者寫操作. 只有當它們真正被關閉後, 對於它們嘗試進行讀或者寫操作將會拋出異常, 並使得問題快速顯現出來.

  而且, 幻想當文件對象析構時, 文件和sockets會自動關閉, 試圖將文件對象的生命週期和文件的狀態綁定在一起的想法, 都是不現實的. 因爲有如下原因:

1.沒有任何方法可以確保運行環境會真正的執行文件的析構.不同的Python實現採用不同的內存管理技術, 比如延時垃圾處理機制. 延時垃圾處理機制可能會導致對象生命週期被任意無限制的延長.
2.對於文件意外的引用,會導致對於文件的持有時間超出預期(比如對於異常的跟蹤, 包含有全局變量等).
  推薦使用 “with”語句 以管理文件:

with open("hello.txt") as hello_file:
    for line in hello_file:
        print line

  對於不支持使用”with”語句的類似文件的對象,使用

contextlib.closing():
import contextlib
with contextlib.closing(urllib.urlopen("http://www.python.org/")) as front_page:
    for line in front_page:
        print line
Legacy AppEngine 中Python 2.5的代碼如使用”with”語句, 需要添加 “from __future__ import with_statement”.

TODO註釋
  爲臨時代碼使用TODO註釋, 它是一種短期解決方案. 不算完美, 但夠好了. 
  TODO註釋應該在所有開頭處包含”TODO”字符串, 緊跟着是用括號括起來的你的名字, email地址或其它標識符. 然後是一個可選的冒號. 接着必須有一行註釋, 解釋要做什麼. 主要目的是爲了有一個統一的TODO格式, 這樣添加註釋的人就可以搜索到(並可以按需提供更多細節). 寫了TODO註釋並不保證寫的人會親自解決問題. 當你寫了一個TODO, 請註上你的名字.

‘# TODO([email protected]): Use a “*” here for string repetition. 
‘# TODO(Zeke) Change this to use relations. 
“`

  如果你的TODO是”將來做某事”的形式, 那麼請確保你包含了一個指定的日期(“2009年11月解決”)或者一個特定的事件(“等到所有的客戶都可以處理XML請求就移除這些代碼”).

導入格式
  每個導入應該獨佔一行 
Yes: import os 
import sys 
No: import os, sys 
  導入總應該放在文件頂部, 位於模塊註釋和文檔字符串之後, 模塊全局變量和常量之前. 導入應該按照從最通用到最不通用的順序分組: 
標準庫導入 
第三方庫導入 
應用程序指定導入 
每種分組中, 應該根據每個模塊的完整包路徑按字典序排序, 忽略大小寫. 
import foo 
from foo import bar 
from foo.bar import baz 
from foo.bar import Quux 
from Foob import ar

語句
通常每個語句應該獨佔一行 
不過, 如果測試結果與測試語句在一行放得下, 你也可以將它們放在同一行. 如果是if語句, 只有在沒有else時才能這樣做. 特別地, 絕不要對 try/except 這樣做, 因爲try和except不能放在同一行. 
Yes: 
if foo: bar(foo) 
No: 
if foo: bar(foo) 
else: baz(foo) 
try: bar(foo) 
except ValueError: baz(foo) 
try: 
bar(foo) 
except ValueError: baz(foo)

訪問控制
  在Python中, 對於瑣碎又不太重要的訪問函數, 你應該直接使用公有變量來取代它們, 這樣可以避免額外的函數調用開銷. 當添加更多功能時, 你可以用屬性(property)來保持語法的一致性. 
  (譯者注: 重視封裝的面向對象程序員看到這個可能會很反感, 因爲他們一直被教育: 所有成員變量都必須是私有的! 其實, 那真的是有點麻煩啊. 試着去接受Pythonic哲學吧) 
  另一方面, 如果訪問更復雜, 或者變量的訪問開銷很顯著, 那麼你應該使用像 get_foo() 和 set_foo() 這樣的函數調用. 如果之前的代碼行爲允許通過屬性(property)訪問 , 那麼就不要將新的訪問函數與屬性綁定. 這樣, 任何試圖通過老方法訪問變量的代碼就沒法運行, 使用者也就會意識到複雜性發生了變化.

命名
module_name, package_name, ClassName, method_name, ExceptionName, function_name, GLOBAL_VAR_NAME, instance_var_name, function_parameter_name, local_var_name. 
應該避免的名稱 
單字符名稱, 除了計數器和迭代器. 
包/模塊名中的連字符(-) 
雙下劃線開頭並結尾的名稱(Python保留, 例如init)

命名約定
  所謂”內部(Internal)”表示僅模塊內可用, 或者, 在類內是保護或私有的. 
  用單下劃線(_)開頭表示模塊變量或函數是protected的(使用import * from時不會包含). 
  用雙下劃線(__)開頭的實例變量或方法表示類內私有. 
  將相關的類和頂級函數放在同一個模塊裏. 不像Java, 沒必要限制一個類一個模塊. 
  對類名使用大寫字母開頭的單詞(如CapWords, 即Pascal風格), 但是模塊名應該用小寫加下劃線的方式(如lower_with_under.py). 儘管已經有很多現存的模塊使用類似於CapWords.py這樣的命名, 但現在已經不鼓勵這樣做, 因爲如果模塊名碰巧和類名一致, 這會讓人困擾.

Python之父Guido推薦的規範
Type Public Internal 
Modules lower_with_under _lower_with_under 
Packages lower_with_under 
Classes CapWords _CapWords 
Exceptions CapWords 
Functions lower_with_under() _lower_with_under() 
Global/Class Constants CAPS_WITH_UNDER _CAPS_WITH_UNDER 
Global/Class Variables lower_with_under _lower_with_under 
Instance Variables lower_with_under _lower_with_under (protected) or __lower_with_under (private) 
Method Names lower_with_under() _lower_with_under() (protected) or __lower_with_under() (private) 
Function/Method Parameters lower_with_under 
Local Variables lower_with_under

Main


if name == ‘main‘: 
main() 
  所有的頂級代碼在模塊導入時都會被執行. 要小心不要去調用函數, 創建對象, 或者執行那些不應該在使用pydoc時執行的操作.

Python進階(十一)-定義實例方法
  一個實例的私有屬性就是以__開頭的屬性,無法被外部訪問。那這些屬性定義有什麼用? 
  雖然私有屬性無法從外部訪問,但是,從類的內部是可以訪問的。除了可以定義實例的屬性外,還可以定義實例的方法。 
  實例的方法就是在類中定義的函數,它的第一個參數永遠是 self,指向調用該方法的實例本身,其他參數和一個普通函數是完全一樣的。

class Person(object):
    def __init__(self, name):
        self.__name = name
 
    def get_name(self):
        return self.__name

  get_name(self) 就是一個實例方法,它的第一個參數是self。init(self, name)其實也可看做是一個特殊的實例方法。 
調用實例方法必須在實例上調用:

p1 = Person('Bob')
print p1.get_name()  # self不需要顯式傳入
# => Bob

  在實例方法內部,可以訪問所有實例屬性,這樣,如果外部需要訪問私有屬性,可以通過方法調用獲得,這種數據封裝的形式除了能保護內部數據一致性外,還可以簡化外部調用的難度。

舉例
  請給 Person 類增加一個私有屬性 __score,表示分數,再增加一個實例方法 get_grade(),能根據 __score 的值分別返回 A-優秀, B-及格, C-不及格三檔。 
注意get_grade()是實例方法,第一個參數爲self。

參考代碼:

class Person(object):
 
    def __init__(self, name, score):
        self.__name = name
        self.__score = score
 
    def get_grade(self):
        if self.__score >= 80:
            return 'A'
        if self.__score >= 60:
            return 'B'
        return 'C'
 
p1 = Person('Bob', 90)
p2 = Person('Alice', 65)
p3 = Person('Tim', 48)
 
print p1.get_grade()
print p2.get_grade()
print p3.get_grade()

運行結果 


Python進階(十二)-淺談python中的方法
  我們在 class 中定義的實例方法其實也是屬性,它實際上是一個函數對象:

class Person(object):
    def __init__(self, name, score):
        self.name = name
        self.score = score
    def get_grade(self):
        return 'A'
 
p1 = Person('Bob', 90)
print p1.get_grade
# => <bound method Person.get_grade of <__main__.Person object at 0x109e58510>>
print p1.get_grade()
# => A

  也就是說,p1.get_grade 返回的是一個函數對象,但這個函數是一個綁定到實例的函數,p1.get_grade() 纔是方法調用。 
因爲方法也是一個屬性,所以,它也可以動態地添加到實例上,只是需要用 types.MethodType() 把一個函數變爲一個方法:

import types
def fn_get_grade(self):
    if self.score >= 80:
        return 'A'
    if self.score >= 60:
        return 'B'
    return 'C'
 
class Person(object):
    def __init__(self, name, score):
        self.name = name
        self.score = score
 
p1 = Person('Bob', 90)
p1.get_grade = types.MethodType(fn_get_grade, p1, Person)
print p1.get_grade()
# => A
p2 = Person('Alice', 65)
print p2.get_grade()
# ERROR: AttributeError: 'Person' object has no attribute 'get_grade'
# 因爲p2實例並沒有綁定get_grade

  給一個實例動態添加方法並不常見,直接在class中定義要更直觀。

舉例
  由於屬性可以是普通的值對象,如 str,int 等,也可以是方法,還可以是函數,大家看看下面代碼的運行結果,請想一想 p1.get_grade 爲什麼是函數而不是方法:

class Person(object):
    def __init__(self, name, score):
        self.name = name
        self.score = score
        self.get_grade = lambda: 'A'
 
p1 = Person('Bob', 90)
print p1.get_grade
print p1.get_grade()

  直接把 lambda 函數賦值給 self.get_grade 和綁定方法有所不同,函數調用不需要傳入 self,但是方法調用需要傳入 self。


Python進階(十三)-淺談sorted 函數應用
  衆所周知,def cmp 作爲方法存在,用sort對實例進行排序時,會用到class 中的cmp。但是,在python3中,取消了 sorted對cmp的支持。 
  python3 中有關排序的sorted方法如下:

sorted(iterable,key=None,reverse=False)

  其中,key接受一個函數,這個函數只接受一個元素,默認爲None。 
reverse是一個布爾值。如果設置爲True,列表元素將被倒序排列,默認爲False。 
  下面着重介紹key的作用原理: 
  key指定一個接收一個參數的函數,這個函數用於從每個元素中提取一個用於比較的關鍵字。默認值爲None 。

例1
students = [('john', 'A', 15), ('jane', 'B', 12), ('dave','B', 10)]
sorted(students,key=lambda s: x[2]) #按照年齡來排序

  結果:[(‘dave’,’B’, 10), (‘jane’, ‘B’, 12), (‘john’, ‘A’, 15)]

例2
  這是一個字符串排序,排序規則:小寫<大寫<奇數<偶數 
s = ‘asdf234GDSdsf23’ #排序:小寫-大寫-奇數-偶數

print("".join(sorted(s, key=lambda x: (x.isdigit(),x.isdigit() and int(x) % 2 == 0,x.isupper(),x))))

  原理:先比較元組的第一個值,如果相等就比較元組的下一個值,以此類推。 
  先看一下Boolean value 的排序: 
  print(sorted([True,Flase]))===>結果[False,True] 
  Boolean 的排序會將 False 排在前,True排在後 .

1.x.isdigit()的作用是把數字放在前邊,字母放在後邊.
2.x.isdigit() and int(x) % 2 == 0的作用是保證奇數在前,偶數在後。
3.x.isupper()的作用是在前面基礎上,保證字母小寫在前大寫在後.
4.最後的x表示在前面基礎上,對所有類別數字或字母排序。
  最後結果:addffssDGS33224

例3
一道面試題:

list1=[7, -8, 5, 4, 0, -2, -5]
#要求1.正數在前負數在後 2.正數從小到大 3.負數從大到小
sorted(list1,key=lambda x:(x<0,abs(x)))

  解題思路:先按照正負排先後,再按照大小排先後。


Python進階(十四)- 基礎課程結課總結:高階函數
  在慕課網完成了廖雪峯老師的《Python入門》與《Python進階》兩門基礎課程。在此做一下簡單的知識點小結。

函數式編程
  Python特點:

不是純函數式編程(允許變量存在);
支持高階函數(可以傳入函數作爲變量);
支持閉包(可以返回函數);
有限度的支持匿名函數;
  高階函數:

變量可以指向函數;
函數的參數可以接收變量;
一個函數可以接收另一個函數作爲參數;
def add(x,y,f):  
    return f(x)+f(y)  
#14
add(-5,9,abs)  

內置高階函數map()
  map函數有兩個參數,一個是函數,另一個是列表,返回值爲對傳入的列表中每一個元素執行傳入的函數操作之後得到的列表;

def format_name(s):  
    return s.title()
 
print map(format_name, ['adam', 'LISA', 'barT'])  

內置高階函數reduce()
  reduce函數也有兩個參數,一個是函數,另一個是列表,返回值爲對list的每一個元素反覆調用函數f,得到最終結果,以下函數爲連乘;

def prod(x, y):  
    return x*y;  
 
print reduce(prod, [2, 4, 5, 7, 12])  

內置高階函數filter()
  filter函數接受函數參數f和列表參數lst,f對lst元素進行判斷,返回lst元素中調用f函數結果爲true的元素組成的列表(將不滿足f函數條件的元素過濾掉);

import math  
 
def is_sqr(x):  
    return int(math.sqrt(x))*int(math.sqrt(x))==x  
 
print filter(is_sqr, range(1, 101))  

自定義排序函數sorted()
  sorted函數接受一個列表lst和一個函數參數f,f爲自定義的比較lst元素大小的函數,返回值爲lst中元素按f函數排列的列表;

def cmp_ignore_case(s1, s2):  
    return cmp(s1.lower(),s2.lower())  
 
print sorted(['bob', 'about', 'Zoo', 'Credit'], cmp_ignore_case)

返回函數:

def calc_prod(lst):  
    def prod(x,y):  
        return x*y;  
    def g():
        return reduce(prod,lst)  
    return g; 
 
f = calc_prod([1, 2, 3, 4])  
print f()  

閉包
  內層函數使用外層函數的參數,然後返回內層函數;

def count():  
    fs = []  
    for i in range(1, 4):  
        def f(j):  
            def g():  
              return j*j;  
            return g  
        fs.append(f(i))  
    return fs  
 
f1, f2, f3 = count()  
print f1(), f2(), f3()  

匿名函數
  傳入函數參數不需要顯式定義函數,可以用lambda x:statement x爲參數,statement爲對參數執行的語句;

def is_not_empty(s):  
    return s and len(s.strip()) > 0  
 
print filter(lambda s:s and len(s.strip())>0, ['test', None, '', 'str', '  ', 'END'])  

裝飾器
  給函數添加新功能,並簡化該函數調用;

無參數裝飾器
def log(f): 
    def fn(*args, **kw): #*args,**kw保證對任意個數參數都能正常調用  
        print 'call ' + f.__name__ + '()...'  
        return f(*args, **kw)
    return fn
 
@log   #調用日誌裝飾器  
def factorial(n):  
    return reduce(lambda x,y: x*y, range(1, n+1))  
#call factorial()...  
#3628800  
print factorial(10)  

帶參數裝飾器
def log(prefix):  
    def log_decorator(f):  
        def wrapper(*args, **kw):  
            print '[%s] %s()...' % (prefix, f.__name__)  
            return f(*args, **kw)  
        return wrapper  
    return log_decorator  
 
@log('DEBUG') #DEBUG爲給裝飾器傳入的參數  
def test():  
    pass  
#[DEBUG] test()...  
#None  
print test()  

  利用functool.wraps作用在返回的新函數上,使得調用裝飾器以後不改變原函數的信息

import time, functools  
def performance(unit):  
    def perf_decorator(f):  
        @functools.wraps(f)  
        def wrapper(*args, **kw):  
            t1 = time.time()  
            r = f(*args, **kw)  
            t2 = time.time()  
            t = (t2 - t1) * 1000 if unit=='ms' else (t2 - t1)  
            print 'call %s() in %f %s' % (f.__name__, t, unit)  
            return r  
        return wrapper  
    return perf_decorator  
 
@performance('ms')  
def factorial(n):  
    return reduce(lambda x,y: x*y, range(1, n+1))  
print factorial.__name__  

偏函數
  functools.partial(f,f的默認參數) 減少需要提供給f的參數

import functools  
int2 = functools.partial(int, base=2)  
int2('1000000')  #64


Python進階(十五)-file文件操作
  Python下文件操作與Java差不多。打開文件的模式有三種:

r,只讀模式(默認)。
w,只寫模式。【不可讀;不存在則創建;存在則刪除內容;因爲會清空原有文件的內容,一定要慎用】
a,追加模式。【可讀; 不存在則創建;存在則只追加內容;】
  注意最後要記得關閉文件:f.close() 
  python只能將字符串寫入到文本文件。要將數值數據存儲到文本本件中,必須先試用函數str()將其轉換爲字符串格式。

#r模式(只讀模式)
f = open('yesterday',encoding='utf-8')
data = f.read()
f.close()
print(data)
#只讀文件的前5行:
f = open('yesterday','r',encoding='utf-8')
for i in range(5):
    print(f.readline())
#以列表的方式讀取這個文件
f = open('yesterday','r',encoding='utf-8')
for line in f.readlines():
    print(line)
#對文件的第3行,做一個特殊的打印(這種方式比較低效,因爲它會先把文件全部讀取到內存中,文件較大時會很影響性能)
f = open('yesterday','r',encoding='utf-8')
for index,line in enumerate(f.readlines()):
    if index == 2:
        print('-----測試線----',line)
        continue
    print(line)
#比較高效的,逐行讀取文件內容,因爲它是一行一行的去讀取文件,不會先把文件一次性全部讀取到內存中
f = open('yesterday','r',encoding='utf-8')
for line in f:
    print(line)
#比較高效的,對文件的3三行進行過特殊打印
f = open('yesterday','r',encoding='utf-8')
count = 0
for line in f:
    if count == 2:
        print('------測試線----:',line)
    count += 1
    print(line)
 
#w模式(如果沒有就創建文件,如果有就清空文件,一定要慎用)
f = open('yesterday','w',encoding='utf-8')
f.write("這就是一行測試\n")
f.write("測試2\n")
f.write("測試3")
 
#a模式(追加模式,如果沒有就創建文件,如果有就把內容追加進去)
f = open('yesterday','a',encoding='utf-8')
f.write("test1\n")
 
#文件光標的操作
f = open('yesterday','r',encoding='utf-8')
#獲得文件的光標
print(f.tell())
print(f.readline())
print(f.tell())
#回到最開始的位置(也可以指定一個位置,前提是你得知道你去往的字符具體在哪個位置才行)
f.seek(0)
 
#顯示文件的編碼
print(f.encoding)
#顯示文件句柄的編號(我並不確定這個說法是否正確,用到時請仔細去查一下)
print(f.fileno())
#測試是否是一個終端設備文件
print(f.isatty())
#把緩存的內容刷新到硬盤(進度條那個腳本里有寫一個示例)
f.flush()
#指定文件從哪裏開始截斷,如果沒有參數,默認從0開始等於清空了這個文件
#f.truncate()
 
#r+ 模式(讀,追加模式)
f = open('yesterday','r+',encoding='utf-8')
data = f.read()
print(data)
f.write("test...\n")
 
#還有 w+寫讀模式, a+追加讀模式,這一般不用,真用到了再去了解下吧
#rb 模式,以二進制的方式讀取這個文件
#wb 模式,二進制寫
'''
'''
#with語句(很實用,記得要經常用喲~,在《Python編程從入門到實戰》那本書裏的文件與異常那一章有詳細用法)
#爲了避免打開文件後忘記關閉,可以通過管理上下文,即:
with open('log','r') as f:
 
    ...
#如此方式,當with代碼塊執行完畢時,內部會自動關閉並釋放文件資源。
#在Python 2.7 後,with又支持同時對多個文件的上下文進行管理,即:
with open('log1') as obj1, open('log2') as obj2:
    pass

參考資料
  http://www.yiibai.com/python3/file_methods.html


Python進階(十六)-正則表達式
  正則表達式是一個特殊的字符序列,它能幫助你方便的檢查一個字符串是否與某種模式匹配。 
  Python 自1.5版本起增加了re 模塊,它提供 Perl 風格的正則表達式模式。 
  在python中使用正則表達式,需要引入re模塊;下面介紹下該模塊中的一些方法;

compile和match
  re模塊中compile用於生成pattern的對象,再通過調用pattern實例的match方法處理文本最終獲得match實例;通過使用match獲得信息;

import re
 
# 將正則表達式編譯成Pattern對象
pattern = re.compile(r'rlovep')
# 使用Pattern匹配文本,獲得匹配結果,無法匹配時將返回None
m = pattern.match('rlovep.com')
if m:
# 使用Match獲得分組信息
    print(m.group())
### 輸出 ###
# rlovep

  re.compile(strPattern[, flag]): 
  這個方法是Pattern類的工廠方法,用於將字符串形式的正則表達式編譯爲Pattern對象。第二個參數flag是匹配模式,取值可以使用按位或運算符’|’表示同時生效,比如re.I | re.M。另外,你也可以在regex字符串中指定模式,比如re.compile(‘pattern’, re.I | re.M)與re.compile(‘(?im)pattern’)是等價的。 
  可選值有:

re.I(re.IGNORECASE): 忽略大小寫(括號內是完整寫法,下同)
M(MULTILINE): 多行模式,改變’^’和’$’的行爲
S(DOTALL): 點任意匹配模式,改變’.’的行爲
L(LOCALE): 使預定字符類 \w \W \b \B \s \S 取決於當前區域設定
U(UNICODE): 使預定字符類 \w \W \b \B \s \S \d \D 取決於unicode定義的字符屬性
X(VERBOSE): 詳細模式。這個模式下正則表達式可以是多行,忽略空白字符,並可以加入註釋。
Match
  Match對象是一次匹配的結果,包含了很多關於此次匹配的信息,可以使用Match提供的可讀屬性或方法來獲取這些信息。 
  屬性:

string: 匹配時使用的文本。
re: 匹配時使用的Pattern對象。
pos: 文本中正則表達式開始搜索的索引。值與Pattern.match()和Pattern.seach()方法的同名參數相同。
endpos: 文本中正則表達式結束搜索的索引。值與Pattern.match()和Pattern.seach()方法的同名參數相同。
lastindex: 最後一個被捕獲的分組在文本中的索引。如果沒有被捕獲的分組,將爲None。
lastgroup: 最後一個被捕獲的分組的別名。如果這個分組沒有別名或者沒有被捕獲的分組,將爲None。
方法:

group([group1, …])
  獲得一個或多個分組截獲的字符串;指定多個參數時將以元組形式返回。group1可以使用編號也可以使用別名;編號0代表整個匹配的子串;不填寫參數時,返回group(0);沒有截獲字符串的組返回None;截獲了多次的組返回最後一次截獲的子串。

groups([default]):
  以元組形式返回全部分組截獲的字符串。相當於調用group(1,2,…last)。default表示沒有截獲字符串的組以這個值替代,默認爲None。

groupdict([default])
返回以有別名的組的別名爲鍵、以該組截獲的子串爲值的字典,沒有別名的組不包含在內。default含義同上。

start([group])
  返回指定的組截獲的子串在string中的起始索引(子串第一個字符的索引)。group默認值爲0。

end([group])
  返回指定的組截獲的子串在string中的結束索引(子串最後一個字符的索引+1)。group默認值爲0。

span([group])
  返回(start(group), end(group))。

expand(template)
  將匹配到的分組代入template中然後返回。template中可以使用\id或\g、 \g引用分組,但不能使用編號0。\id與\g是等價的;但\10將被認爲是第10個分組,如果你想表達 \1之後是字符’0’,只能使用\g<1>0。

pattern
  Pattern對象是一個編譯好的正則表達式,通過Pattern提供的一系列方法可以對文本進行匹配查找。 
  Pattern不能直接實例化,必須使用re.compile()進行構造。 
  Pattern提供了幾個可讀屬性用於獲取表達式的相關信息:

pattern: 編譯時用的表達式字符串。
flags: 編譯時用的匹配模式。數字形式。
groups: 表達式中分組的數量。
groupindex: 以表達式中有別名的組的別名爲鍵、以該組對應的編號爲值的字典,沒有別名的組不包含在內。
實例方法[ | re模塊方法]:

match(string[, pos[, endpos]]) | re.match(pattern, string[, flags])
  這個方法將從string的pos下標處起嘗試匹配pattern;如果pattern結束時仍可匹配,則返回一個Match對象;如果匹配過程中pattern無法匹配,或者匹配未結束就已到達endpos,則返回None。 
pos和endpos的默認值分別爲0和len(string);re.match()無法指定這兩個參數,參數flags用於編譯pattern時指定匹配模式。 
  注意:這個方法並不是完全匹配。當pattern結束時若string還有剩餘字符,仍然視爲成功。想要完全匹配,可以在表達式末尾加上邊界匹配符’$’。

search(string[, pos[, endpos]]) | re.search(pattern, string[, flags])
  這個方法用於查找字符串中可以匹配成功的子串。從string的pos下標處起嘗試匹配pattern,如果pattern結束時仍可匹配,則返回一個Match對象;若無法匹配,則將pos加1重新嘗試匹配;直到pos=endpos時仍無法匹配則返回None。 pos和endpos的默認值分別爲0和len(string));re.search()無法指定這兩個參數,參數flags用於編譯pattern時指定匹配模式。

split(string[, maxsplit]) | re.split(pattern, string[, maxsplit])
  按照能夠匹配的子串將string分割後返回列表。maxsplit用於指定最大分割次數,不指定將全部分割。

findall(string[, pos[, endpos]]) | re.findall(pattern, string[, flags])
  搜索string,以列表形式返回全部能匹配的子串。

finditer(string[, pos[, endpos]]) | re.finditer(pattern, string[, flags])
  搜索string,返回一個順序訪問每一個匹配結果(Match對象)的迭代器。

sub(repl, string[, count]) | re.sub(pattern, repl, string[, count])
  使用repl替換string中每一個匹配的子串後返回替換後的字符串。 當repl是一個字符串時,可以使用\id或\g、\g引用分組,但不能使用編號0。 當repl是一個方法時,這個方法應當只接受一個參數(Match對象),並返回一個字符串用於替換(返回的字符串中不能再引用分組)。 count用於指定最多替換次數,不指定時全部替換。

subn(repl, string[, count]) |re.sub(pattern, repl, string[, count])
  返回 (sub(repl, string[, count]), 替換次數)。

re.match函數
  re.match 嘗試從字符串的起始位置匹配一個模式,如果不是起始位置匹配成功的話,match()就返回none。 
  函數語法: 
  re.match(pattern, string, flags=0) 
  函數參數說明: 

匹配成功re.match方法返回一個匹配的對象,否則返回None。 
我們可以使用group(num) 或 groups() 匹配對象函數來獲取匹配表達式。 


實例 1
#!/usr/bin/python
# -*- coding: UTF-8 -*- 
 
import re
print(re.match('www', 'www.runoob.com').span())  # 在起始位置匹配
print(re.match('com', 'www.runoob.com'))         # 不在起始位置匹配

  以上實例運行輸出結果爲: 
  (0, 3) 
  None

實例 2
#!/usr/bin/python3
import re
 
line = "Cats are smarter than dogs"
 
matchObj = re.match( r'(.*) are (.*?) .*', line, re.M|re.I)
 
if matchObj:
   print ("matchObj.group() : ", matchObj.group())
   print ("matchObj.group(1) : ", matchObj.group(1))
   print ("matchObj.group(2) : ", matchObj.group(2))
else:
   print ("No match!!")

  以上實例執行結果如下: 
  matchObj.group() : Cats are smarter than dogs 
  matchObj.group(1) : Cats 
  matchObj.group(2) : smarter

re.search方法
  re.search 掃描整個字符串並返回第一個成功的匹配。 
  函數語法: 
  re.search(pattern, string, flags=0) 
  函數參數說明: 

匹配成功re.search方法返回一個匹配的對象,否則返回None。 
我們可以使用group(num) 或 groups() 匹配對象函數來獲取匹配表達式。 


實例一
import re
print(re.search("rlovep","rlovep.com").span())
print(re.search("com","http://rlovep.com").span())

實例二
import re
line = "This is my blog"
#匹配含有is的字符串
matchObj = re.search( r'(.*) is (.*?) .*', line, re.M|re.I)
#使用了組輸出:當group不帶參數是將整個匹配成功的輸出
#當帶參數爲1時匹配的是最外層左邊包括的第一個括號,一次類推;
if matchObj:
    print ("matchObj.group() : ", matchObj.group())#匹配整個
    print ("matchObj.group(1) : ", matchObj.group(1))#匹配的第一個括號
    print ("matchObj.group(2) : ", matchObj.group(2))#匹配的第二個括號
else:
    print ("No match!!")

  輸出: 
  matchObj.group() : This is my blog 
  matchObj.group(1) : This 
  matchObj.group(2) : my

search和match區別
  re.match只匹配字符串的開始,如果字符串開始不符合正則表達式,則匹配失敗,函數返回None;而re.search匹配整個字符串,直到找到一個匹配。

實例
#!/usr/bin/python3
 
import re
 
line = "Cats are smarter than dogs";
 
matchObj = re.match( r'dogs', line, re.M|re.I)
if matchObj:
   print ("match --> matchObj.group() : ", matchObj.group())
else:
   print ("No match!!")
 
matchObj = re.search( r'dogs', line, re.M|re.I)
if matchObj:
   print ("search --> matchObj.group() : ", matchObj.group())
else:
   print ("No match!!")

  以上實例運行結果如下: 
  No match!! 
  search –> matchObj.group() : dogs

檢索和替換
  Python 的re模塊提供了re.sub用於替換字符串中的匹配項。 
  語法: 
  re.sub(pattern, repl, string, count=0) 
  參數:

pattern : 正則中的模式字符串。
repl : 替換的字符串,也可爲一個函數。
string : 要被查找替換的原始字符串。
count : 模式匹配後替換的最大次數,默認 0 表示替換所有的匹配。
實例
#!/usr/bin/python3
import re
 
phone = "2004-959-559 # 這是一個電話號碼"
 
# 刪除註釋
num = re.sub(r'#.*$', "", phone)
print ("電話號碼 : ", num)
 
# 移除非數字的內容
num = re.sub(r'\D', "", phone)
print ("電話號碼 : ", num) 

  以上實例執行結果如下: 
  電話號碼 : 2004-959-559 
  電話號碼 : 2004959559 
  repl 參數是一個函數 
  以下實例中將字符串中的匹配的數字乘於 2:

#!/usr/bin/python
 
import re
 
# 將匹配的數字乘於 2
def double(matched):
    value = int(matched.group('value'))
    return str(value * 2)
 
s = 'A23G4HFD567'
print(re.sub('(?P<value>\d+)', double, s))

  執行輸出結果爲: 
  A46G8HFD1134

正則表達式修飾符 - 可選標誌
  正則表達式可以包含一些可選標誌修飾符來控制匹配的模式。修飾符被指定爲一個可選的標誌。多個標誌可以通過按位 OR(|) 它們來指定。如 re.I | re.M 被設置成 I 和 M 標誌: 


正則表達式模式
  模式字符串使用特殊的語法來表示一個正則表達式:

字母和數字表示他們自身。一個正則表達式模式中的字母和數字匹配同樣的字符串。
多數字母和數字前加一個反斜槓時會擁有不同的含義。
標點符號只有被轉義時才匹配自身,否則它們表示特殊的含義。
反斜槓本身需要使用反斜槓轉義。
由於正則表達式通常都包含反斜槓,所以你最好使用原始字符串來表示它們。模式元素(如 r’/t’,等價於’//t’)匹配相應的特殊字符。
  下表列出了正則表達式模式語法中的特殊元素。如果你使用模式的同時提供了可選的標誌參數,某些模式元素的含義會改變。 


正則表達式實例
字符匹配 

參考資料
  http://www.runoob.com/python3/python3-reg-expressions.html


Python進階(十七)-Python 字符串操作
去空格及特殊符號
s.strip().lstrip().rstrip(',')

複製字符串
#strcpy(sStr1,sStr2)
sStr1 = 'strcpy'
sStr2 = sStr1
sStr1 = 'strcpy2'
print sStr2

連接字符串
#strcat(sStr1,sStr2)
sStr1 = 'strcat'
sStr2 = 'append'
sStr1 += sStr2
print sStr1

查找字符
#strchr(sStr1,sStr2)
# < 0 爲未找到
sStr1 = 'strchr'
sStr2 = 's'
nPos = sStr1.index(sStr2)
print nPos

比較字符串
#strcmp(sStr1,sStr2)
sStr1 = 'strchr'
sStr2 = 'strch'
print cmp(sStr1,sStr2)

掃描字符串是否包含指定的字符
#strspn(sStr1,sStr2)
sStr1 = '12345678'
sStr2 = '456'
#sStr1 and chars both in sStr1 and sStr2
print len(sStr1 and sStr2)

字符串長度
#strlen(sStr1)
sStr1 = 'strlen'
print len(sStr1)

將字符串中的大小寫轉換
#strlwr(sStr1)
sStr1 = 'JCstrlwr'
sStr1 = sStr1.upper()
#sStr1 = sStr1.lower()
print sStr1

追加指定長度的字符串
#strncat(sStr1,sStr2,n)
sStr1 = '12345'
sStr2 = 'abcdef'
n = 3
sStr1 += sStr2[0:n]
print sStr1

字符串指定長度比較
#strncmp(sStr1,sStr2,n)
sStr1 = '12345'
sStr2 = '123bc'
n = 3
print cmp(sStr1[0:n],sStr2[0:n])

複製指定長度的字符
#strncpy(sStr1,sStr2,n)
sStr1 = ''
sStr2 = '12345'
n = 3
sStr1 = sStr2[0:n]
print sStr1

將字符串前n個字符替換爲指定的字符
#strnset(sStr1,ch,n)
sStr1 = '12345'
ch = 'r'
n = 3
sStr1 = n * ch + sStr1[3:]
print sStr1

掃描字符串
#strpbrk(sStr1,sStr2)
sStr1 = 'cekjgdklab'
sStr2 = 'gka'
nPos = -1
for c in sStr1:
    if c in sStr2:
        nPos = sStr1.index(c)
        break
print nPos

翻轉字符串
#strrev(sStr1)
sStr1 = 'abcdefg'
sStr1 = sStr1[::-1]
print sStr1

查找字符串
#strstr(sStr1,sStr2)
sStr1 = 'abcdefg'
sStr2 = 'cde'
print sStr1.find(sStr2)

分割字符串
#strtok(sStr1,sStr2)
sStr1 = 'ab,cde,fgh,ijk'
sStr2 = ','
sStr1 = sStr1[sStr1.find(sStr2) + 1:]
print sStr1
#或者
s = 'ab,cde,fgh,ijk'
print(s.split(','))

連接字符串
delimiter = ','
mylist = ['Brazil', 'Russia', 'India', 'China']
print delimiter.join(mylist)

截取字符串
str = ’0123456789′
print str[0:3] #截取第一位到第三位的字符
print str[:] #截取字符串的全部字符
print str[6:] #截取第七個字符到結尾
print str[:-3] #截取從頭開始到倒數第三個字符之前
print str[2] #截取第三個字符
print str[-1] #截取倒數第一個字符
print str[::-1] #創造一個與原字符串順序相反的字符串
print str[-3:-1] #截取倒數第三位與倒數第一位之前的字符
print str[-3:] #截取倒數第三位到結尾
print str[:-5:-3] #逆序截取,具體啥意思沒搞明白?

python字符串跟整型互轉
print ("整型:",int(50))
a=int(50)
print("整型:",type(a))
numStr = "50";
print ("字符串:",type(numStr))
convertedInt = int(numStr);
print("字符串轉換爲整型:",convertedInt)
convertedstr=str(a)
print("整型轉換爲字符串:",convertedInt)
 


參考資料
  http://www.runoob.com/python3/python3-string.html


Python進階(十八)-Python3爬蟲實踐
  這篇文章主要介紹瞭如何使用Python3爬取csdn博客訪問量的相關資料,在Python2已實現的基礎上實現Python3爬蟲,對比版本之間的差異所在,需要的朋友可以參考下。 
  使用python來獲取自己博客的訪問量,也是後面將要開發項目的一部分,後邊會對博客的訪問量進行分析,以折線圖和餅圖等可視化的方式展示自己博客被訪問的情況,使自己能更加清楚自己的哪些博客更受關注。其實,在較早之前博客專家本身就有這個功能,不知什麼原因此功能被取消了。

一.網址分析

  進入自己的博客頁面,網址爲:http://blog.csdn.net/sunhuaqiang1。 網址還是比較好分析的:就是csdn的網址+個人csdn登錄賬號,我們來看下一頁的網址。 

  看到第二頁的地址爲:http://blog.csdn.net/sunhuaqiang1/article/list/2後邊的數字表示現在正處於第幾頁,再用其他的頁面驗證一下,確實是這樣的,那麼第一頁爲什麼不是http://blog.csdn.net/sunhuaqiang1/article/list/1呢,那麼我們在瀏覽器中輸http://blog.csdn.net/sunhuaqiang1/article/list/1試試,哎,果然是第一頁啊,第一頁原來是被重定向了,http://blog.csdn.net/sunhuaqiang1被重定向到http://blog.csdn.net/sunhuaqiang1/article/list/1,所以兩個網址都能訪問第一頁,那麼現在規律就非常明顯了: http://blog.csdn.net/sunhuaqiang1/article/list/ + 頁號

二.獲取標題
  右鍵查看網頁的源代碼,我們看到可以找到這樣一段代碼: 
 
 
  我們可以看到標題都是在標籤

<span class="link_title">
<a href="/sunhuaqiang1/article/details/50651235">
...
</a>
</span>

  中的。所以我們可以使用下面的正則表達式來匹配標題:

<span class="link_title"><a href=".*?">(.*?)</a></span>

三.獲取訪問量
  拿到了標題之後,就要獲得對應的訪問量了,經過對源碼的分析,我看到訪問量的結構都是這樣的:

<span class="link_view" title="閱讀次數"><a href="/sunhuaqiang1/article/details/51289580" title="閱讀次數">閱讀</a>(12718)</span>

  括號中的數字即爲訪問量,我們可以用下面的正則表達式來匹配:

<span class="link_view".*?><a href=".*?" title="閱讀次數">閱讀</a>\((.*?)\)</span>

  其中,’.?’的含義是啓用正則懶惰模式。必須跟在或者+後邊用。 
  如:“< img src=”test.jpg” width=”60px” height=”80px”/>” 
  如果用正則匹配src中內容非懶惰模式匹配

src=".*"

  匹配結果是:src=”test.jpg” width=”60px” height=”80px” 
  意思是從=”往後匹配,直到最後一個”匹配結束 
懶惰模式正則:

src=".*?"

  結果:src=”test.jpg” 
  因爲匹配到第一個”就結束了一次匹配。不會繼續向後匹配。因爲他懶惰嘛。

.表示除\n之外的任意字符
*表示匹配0-無窮
+表示匹配1-無窮
四.尾頁判斷
  接下來我們要判斷當前頁是否爲最後一頁,否則我們就不能判斷什麼時候結束了,我找到了源碼中‘尾頁’的標籤,發現是下面的結構:

<a href="/sunhuaqiang1/article/list/2">下一頁</a> <a href="/sunhuaqiang1/article/list/7">尾頁</a>

  所以我們可以用下面的正則表達式來匹配,如果匹配成功就說明當前頁不是最後一頁,否則當前頁就是最後一頁。

<a href=".*?">尾頁</a>

五.編程實現
  下面是摘自的Python2版完整的代碼實現:

#!usr/bin/python
# -*- coding: utf-8 -*-
'''
Created on 2016年2月13日
@author: ***
使用python爬取csdn個人博客的訪問量,主要用來練手
'''
import urllib2
import re
#當前的博客列表頁號
page_num = 1
#不是最後列表的一頁
notLast = 1
account = str(raw_input('輸入csdn的登錄賬號:'))
while notLast:
    #首頁地址
    baseUrl = 'http://blog.csdn.net/'+account
    #連接頁號,組成爬取的頁面網址
    myUrl = baseUrl+'/article/list/'+str(page_num)
    #僞裝成瀏覽器訪問,直接訪問的話csdn會拒絕
    user_agent = 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)'
    headers = {'User-Agent':user_agent}
    #構造請求
    req = urllib2.Request(myUrl,headers=headers)
    #訪問頁面
    myResponse = urllib2.urlopen(req)
    myPage = myResponse.read()
    #在頁面中查找是否存在‘尾頁'這一個標籤來判斷是否爲最後一頁
    notLast = re.findall('<a href=".*?">尾頁</a>',myPage,re.S)
    print '---------------第%d頁-------------' % (page_num,)
    #利用正則表達式來獲取博客的標題
    title = re.findall('<span class="link_title"><a href=".*?">(.*?)</a></span>',myPage,re.S)
    titleList=[]
    for items in title:
        titleList.append(str(items).lstrip().rstrip())
    #利用正則表達式獲取博客的訪問量
    view = re.findall('<span class="link_view".*?><a href=".*?" title="閱讀次數">閱讀</a>\((.*?)\)</span>',myPage,re.S)
    viewList=[]
for items in view:
    viewList.append(str(items).lstrip().rstrip())
#將結果輸出 
for n in range(len(titleList)):
    print '訪問量:%s 標題:%s' % (viewList[n].zfill(4),titleList[n])
#頁號加1
page_num = page_num + 1

  由於自己現在的IDE爲Python3,且自己在學習Python3。故在此基礎上實現Python2項目的升級改造,並在改造過程中發現版本之間的差異性。以下爲Python3版本下的爬蟲代碼。

#!usr/bin/python
# -*- coding: utf-8 -*-
'''
Created on 2017年3月19日
@author: SUN HuaQiang
目的:使用python爬取csdn個人博客的訪問量,主要用來練手Python爬蟲
收穫:1.瞭解Python爬蟲的基本過程
      2.在Python2的基礎上實現Python3,通過對比發現版本之間的差異
'''
import urllib.request
import urllib
import re
 
#當前的博客列表頁號
page_num = 1
#初始化最後列表的頁碼
notLast = 1
account = str(input('請輸入csdn的登錄賬號:'))
while notLast:
    #首頁地址
    baseUrl = 'http://blog.csdn.net/' + account
    #連接頁號,組成爬取的頁面網址
    myUrl = baseUrl+'/article/list/' + str(page_num)
    #僞裝成瀏覽器訪問,直接訪問的話csdn會拒絕
    user_agent = 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)'
    headers = {'User-Agent':user_agent}
    #構造請求
    req = urllib.request.Request(myUrl,headers=headers)
    #訪問頁面
    myResponse = urllib.request.urlopen(req)
    #python3中urllib.read返回的是bytes對象,不是string,得把它轉換成string對象,用bytes.decode方法
    myPage = myResponse.read().decode()
    #在頁面中查找是否存在‘尾頁'這一個標籤來判斷是否爲最後一頁
    notLast = re.findall('<a href=".*?">尾頁</a>', myPage, re.S)
    print ('-----------第%d頁--------------' % (page_num,))
    #利用正則表達式來獲取博客的標題
    title = re.findall('<span class="link_title"><a href=".*?">(.*?)</a></span>',myPage,re.S)
    titleList=[]
    for items in title:
        titleList.append(str(items).lstrip().rstrip())
    #利用正則表達式獲取博客的訪問量
    view = re.findall('<span class="link_view".*?><a href=".*?" title="閱讀次數">閱讀</a>\((.*?)\)</span>',myPage,re.S)
    viewList=[]
    for items in view:
        viewList.append(str(items).lstrip().rstrip())
    #將結果輸出
    for n in range(len(titleList)):
        print ('訪問量:%s 標題:%s' % (viewList[n].zfill(4),titleList[n]))
    #頁號加1
    page_num = page_num + 1

  下面是部分結果: 
 
  瑕疵:通過爬蟲結果可以發現,在CSDN中,對於設置爲指定的文章,爬取結果存在一定的問題,還包含部分css代碼。 
  改善:通過更改獲取博文標題的正則表達式,即可解決此問題。 
 
 
” 
  想法是好的,但是直接利用正則實現標題獲取時遇到了困難,暫時並未實現理想結果。 
  遂改變思路,將利用正則獲取後的字符串再進行二次正則,即替換操作,語句如下:

for items in title:
    titleList.append(re.sub('<font color="red">.*?</font>', '', str(items).lstrip().rstrip()))

  更改後的結果如下。並同時爲每篇博文進行了編號。 
 
  同時,自己還希望獲取到的信息包括:訪問總量、積分、等級、排名、粉絲、原創、轉載、譯文、評論等數據信息。 
  以上信息在網頁源碼中如下所示。

<ul id="blog_rank"> 
    <li>訪問:<span>459285次</span></li> 
    <li>積分:<span>9214</span> </li> 
    <li >等級: <span style="position:relative;display:inline-block;z-index:1" > 
    <img src="http://c.csdnimg.cn/jifen/images/xunzhang/jianzhang/blog6.png" alt="" style="vertical-align: middle;" id="leveImg"> 
    <div id="smallTittle" style=" position: absolute; left: -24px; top: 25px; text-align: center; width: 101px; height: 32px; background-color: #fff; line-height: 32px; border: 2px #DDDDDD solid; box-shadow: 0px 2px 2px rgba (0,0,0,0.1); display: none; z-index: 999;"> 
    <div style="left: 42%; top: -8px; position: absolute; width: 0; height: 0; border-left: 10px solid transparent; border-right: 10px solid transparent; border-bottom: 8px solid #EAEAEA;"></div> 
    積分:9214 </div> 
    </span> </li> 
    <li>排名:<span>第1639名</span></li> 
    </ul> 
    <ul id="blog_statistics"> 
    <li>原創:<span>425篇</span></li> 
    <li>轉載:<span>44篇</span></li> 
    <li>譯文:<span>2篇</span></li> 
    <li>評論:<span>108條</span></li> 
    </ul> 

  則獲取訪問信息的正則表達式爲:

#利用正則表達式獲取博客信息
sumVisit = re.findall('<li>訪問:<span>(.*?)</span></li>', myPage, re.S)
credit = re.findall('<li>積分:<span>(.*?)</span> </li>', myPage, re.S)
rank = re.findall('<li>排名:<span>(.*?)</span></li>', myPage, re.S)
grade = re.findall('<li >.*?<img src=.*?/blog(.*?).png.*?>.*?</li>', test3, re.S)
original = re.findall('<li>原創:<span>(.*?)</span></li>', myPage, re.S)
reprint = re.findall('<li>轉載:<span>(.*?)</span></li>', myPage, re.S)
trans = re.findall('<li>譯文:<span>(.*?)</span></li>', myPage, re.S)
comment = re.findall('<li>評論:<span>(.*?)</span></li>', myPage, re.S)
 

  根據網頁源碼,可得出其正則表達式爲

staData = re.findall('<li><a href=.*?>(.*?)</a><span>(.*?)</span></li>', myPage, re.S)
 
for i in staData:
    print(i[0] + ':' + i[1].lstrip('(').rstrip(')')+'篇')

  經過以上操作,得到的用戶Blog信息如下圖所示: 
 
  最終遇到的問題是:有關粉絲數的爬取遇到了問題,因爲前面數據信息的獲取不需要用戶登錄,而用戶粉絲數是在用戶已登錄情景下獲取的,故需要將用戶登錄信息添加進去。犯愁~ 
  PS:論文盲審送回來了,自己這段時間要用來修改論文了,後面的博客後面再說吧~

注意事項
urllib2在3.5中爲urllib.request;
raw_input()在3.5中爲input();
python3中urllib.read()返回的是bytes對象,不是string,得把它轉換成string對象,用bytes.decode()方法;
re.S意在使.匹配包括換行在內的所有字符;
python3對urllib和urllib2進行了重構,拆分成了urllib.request, urllib.response,urllib.parse,urllib.error等幾個子模塊,這樣的架構從邏輯和結構上說更加合理。urljoin現在對應的函數是urllib.parse.urljoin
  注:Python2部分的爬蟲代碼爲網絡獲取,在此向匿名人士表示感謝。


Python進階(十九)-Python3安裝第三方爬蟲庫BeautifulSoup4
  在做Python3爬蟲練習時,從網上找到了一段代碼如下:

#使用第三方庫BeautifulSoup,用於從html或xml中提取數據
from bs4 import BeautifulSoup

  自己實踐後,發現出現了錯誤,如下所示: 
 
  以上錯誤提示是說沒有發現名爲“bs4”的模塊。即“bs4”模塊未安裝。 
  進入Python安裝目錄,以作者IDE爲例, 
 
 
  控制檯提示第三方庫BeautifulSoup4安裝成功!回到之前的程序中,會發現IntelJ已經檢測到第三方庫BS4的安裝,已自更新項目,此時項目恢復正常,無錯誤提示。

常見問題
   在做BS4爬蟲練習時,新建的文件名爲bs4.py,結果出現如下錯誤提示: 
 
  即ImportError: cannot import name BeautifulSoup一般有一下幾種情況: 
   1. 在python2.x下安裝的BeautifulSoup在python3.x下運行會報這種錯,可用pip3 install Beautifulsoup4 . 
   2. 導入時指定bs4 像這樣: from bs4 import BeautifulSoup. 
   3. 太巧合,如果你測試的文件名正好命名爲bs4.py,那怎麼整都會報這個錯,把名字改成其他的吧。


  BS4官方文檔 

Python進階(二十)-Python爬蟲實例講解
  本篇博文主要講解Python爬蟲實例,重點包括爬蟲技術架構,組成爬蟲的關鍵模塊:URL管理器、HTML下載器和HTML解析器。

爬蟲簡單架構


程序入口函數(爬蟲調度段)
#coding:utf8
import time, datetime
 
from maya_Spider import url_manager, html_downloader, html_parser, html_outputer
 
 
class Spider_Main(object):
    #初始化操作
    def __init__(self):
        #設置url管理器
        self.urls = url_manager.UrlManager()
        #設置HTML下載器
        self.downloader = html_downloader.HtmlDownloader()
        #設置HTML解析器
        self.parser = html_parser.HtmlParser()
        #設置HTML輸出器
        self.outputer = html_outputer.HtmlOutputer()
 
    #爬蟲調度程序
    def craw(self, root_url):
        count = 1
        self.urls.add_new_url(root_url)
        while self.urls.has_new_url():
            try:
                new_url = self.urls.get_new_url()
                print('craw %d : %s' % (count, new_url))
                html_content = self.downloader.download(new_url)
                new_urls, new_data = self.parser.parse(new_url, html_content)
                self.urls.add_new_urls(new_urls)
                self.outputer.collect_data(new_data)
 
                if count == 10:
                    break
 
                count = count + 1
            except:
                print('craw failed')
 
        self.outputer.output_html()
 
if __name__ == '__main__':
    #設置爬蟲入口
    root_url = 'http://baike.baidu.com/view/21087.htm'
    #開始時間
    print('開始計時..............')
    start_time = datetime.datetime.now()
    obj_spider = Spider_Main()
    obj_spider.craw(root_url)
    #結束時間
    end_time = datetime.datetime.now()
    print('總用時:%ds'% (end_time - start_time).seconds)

URL管理器
class UrlManager(object):
    def __init__(self):
        self.new_urls = set()
        self.old_urls = set()
 
    def add_new_url(self, url):
        if url is None:
            return
        if url not in self.new_urls and url not in self.old_urls:
            self.new_urls.add(url)
 
    def add_new_urls(self, urls):
        if urls is None or len(urls) == 0:
            return
        for url in urls:
            self.add_new_url(url)
 
    def has_new_url(self):
        return len(self.new_urls) != 0
 
    def get_new_url(self):
        new_url = self.new_urls.pop()
        self.old_urls.add(new_url)
        return new_url

網頁下載器
import urllib
import urllib.request
 
class HtmlDownloader(object):
 
    def download(self, url):
        if url is None:
            return None
 
        #僞裝成瀏覽器訪問,直接訪問的話csdn會拒絕
        user_agent = 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)'
        headers = {'User-Agent':user_agent}
        #構造請求
        req = urllib.request.Request(url,headers=headers)
        #訪問頁面
        response = urllib.request.urlopen(req)
        #python3中urllib.read返回的是bytes對象,不是string,得把它轉換成string對象,用bytes.decode方法
        return response.read().decode()

網頁解析器
import re
import urllib
from urllib.parse import urlparse
 
from bs4 import BeautifulSoup
 
class HtmlParser(object):
 
    def _get_new_urls(self, page_url, soup):
        new_urls = set()
        #/view/123.htm
        links = soup.find_all('a', href=re.compile(r'/item/.*?'))
        for link in links:
            new_url = link['href']
            new_full_url = urllib.parse.urljoin(page_url, new_url)
            new_urls.add(new_full_url)
        return new_urls
 
    #獲取標題、摘要
    def _get_new_data(self, page_url, soup):
        #新建字典
        res_data = {}
        #url
        res_data['url'] = page_url
        #<dd class="lemmaWgt-lemmaTitle-title"><h1>Python</h1>獲得標題標籤
        title_node = soup.find('dd', class_="lemmaWgt-lemmaTitle-title").find('h1')
        print(str(title_node.get_text()))
        res_data['title'] = str(title_node.get_text())
        #<div class="lemma-summary" label-module="lemmaSummary">
        summary_node = soup.find('div', class_="lemma-summary")
        res_data['summary'] = summary_node.get_text()
 
        return res_data
 
    def parse(self, page_url, html_content):
        if page_url is None or html_content is None:
            return None
 
        soup = BeautifulSoup(html_content, 'html.parser', from_encoding='utf-8')
        new_urls = self._get_new_urls(page_url, soup)
        new_data = self._get_new_data(page_url, soup)
        return new_urls, new_data

網頁輸出器
class HtmlOutputer(object):
 
    def __init__(self):
        self.datas = []
 
    def collect_data(self, data):
        if data is None:
            return
        self.datas.append(data )
 
    def output_html(self):
        fout = open('maya.html', 'w', encoding='utf-8')
        fout.write("<head><meta http-equiv='content-type' content='text/html;charset=utf-8'></head>")
        fout.write('<html>')
        fout.write('<body>')
        fout.write('<table border="1">')
        # <th width="5%">Url</th>
        fout.write('''<tr style="color:red" width="90%">
                    <th>Theme</th>
                    <th width="80%">Content</th>
                    </tr>''')
        for data in self.datas:
            fout.write('<tr>\n')
            # fout.write('\t<td>%s</td>' % data['url'])
            fout.write('\t<td align="center"><a href=\'%s\'>%s</td>' % (data['url'], data['title']))
            fout.write('\t<td>%s</td>\n' % data['summary'])
            fout.write('</tr>\n')
        fout.write('</table>')
        fout.write('</body>')
        fout.write('</html>

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章