Python學習筆記- 廖雪峰教程【python 2】//為繼續學習爬蟲準備-02 [待完善]
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)
模組和包的概念 將所有程式碼放入一個py檔案:無法維護 如果將程式碼分拆放入多個py檔案,好處:1. 同一個名字的變數互不影響 模組多了可能重名,不過只要放到不同的包下面就可以解決 在檔案系統中,包就是資料夾,模組就是xxx.py檔案 區分目錄和包檔案:包資料夾下一定有__init__.py 每層都要有,即使是一個空檔案
如果使用 from...import 匯入 log 函式,勢必引起衝突。這時,可以給函式起個“別名”來避免衝突:
from math import logfrom logging import log as logger # logging的log現在變成了logger
print log(10) # 呼叫的是math的log
logger(10, 'import from logging') # 呼叫的是logging的log
動態匯入模組
如果匯入的模組不存在,Python直譯器會報 ImportError 錯誤:
>>> import something
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ImportError: No module named something
有的時候,兩個不同的模組提供了相同的功能,比如 StringIO
這是因為Python是動態語言,解釋執行,因此Python程式碼執行速度慢。
如果要提高Python程式碼的執行速度,最簡單的方法是把某些關鍵函式用 C 語言重寫,這樣就能大大提高執行速度。
同樣的功能,StringIO 是純Python程式碼編寫的,而 cStringIO 部分函式是 C 寫的,因此 cStringIO 執行速度更快。
利用ImportError錯誤,我們經常在Python中動態匯入模組:
try:
from cStringIO import StringIO
except ImportError:
from StringIO import StringIO
上述程式碼先嚐試從cStringIO匯入,如果失敗了(比如cStringIO沒有被安裝),再嘗試從StringIO匯入。這樣,如果cStringIO模組存在,則我們將獲得更快的執行速度,如果cStringIO不存在,則頂多程式碼執行速度會變慢,但不會影響程式碼的正常執行。
try 的作用是捕獲錯誤,並在捕獲到指定錯誤時執行 except 語句。任務
利用import ... as ...,還可以動態匯入不同名稱的模組。
Python 2.6/2.7提供了json 模組,但Python 2.5以及更早版本沒有json模組,不過可以安裝一個simplejson模組,這兩個模組提供的函式簽名和功能都一模一樣。
試寫出匯入json 模組的程式碼,能在Python 2.5/2.6/2.7都正常執行。 try: import json except ImportError: import simplejson as json print json.dumps({'python':2.7})使用__future__
Python的新版本會引入新的功能,但是,實際上這些功能在上一個老版本中就已經存在了。要“試用”某一新的特性,就可以通過匯入__future__模組的某些功能來實現。
例如,Python 2.7的整數除法運算結果仍是整數:
>>> 10 / 3 3
但是,Python 3.x已經改進了整數的除法運算,“/”除將得到浮點數,“//”除才仍是整數:
>>> 10 / 3 3.3333333333333335 >>> 10 // 3 3
要在Python 2.7中引入3.x的除法規則,匯入__future__的division:
>>> from __future__ import division >>> print 10 / 3 3.3333333333333335當新版本的一個特性與舊版本不相容時,該特性將會在舊版本中新增到__future__中,以便舊的程式碼能在舊版本中測試新特性。 python提供的模組管理功能 -easy_install, -pip 面向物件程式設計的基本思想:類和例項 面向物件程式設計:資料封裝
定義類並建立例項
在Python中,類通過 class 關鍵字定義。以 Person 為例,定義一個Person類如下:
class Person(object): pass
按照 Python 的程式設計習慣,類名以大寫字母開頭,緊接著是(object),表示該類是從哪個類繼承下來的。類的繼承將在後面的章節講解,現在我們只需要簡單地從object類繼承。
有了Person類的定義,就可以創建出具體的xiaoming、xiaohong等例項。建立例項使用 類名+(),類似函式呼叫的形式建立:
xiaoming = Person()xiaohong = Person()
class Person(object):
pass
p1 = Person()
p1.name = 'Bart'
p2 = Person()
p2.name = 'Adam'
p3 = Person()
p3.name = 'Lisa'
L1 = [p1, p2, p3]
L2 = sorted(L1,key=lambda x:x.name)
print L2[0].name
print L2[1].name
print L2[2].name
初始化例項屬性
雖然我們可以自由地給一個例項繫結各種屬性,但是,現實世界中,一種型別的例項應該擁有相同名字的屬性。例如,Person類應該在建立的時候就擁有 name、gender 和 birth 屬性,怎麼辦?
在定義 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')
有了__init__()方法,每個Person例項在建立時,都會有 name、gender 和 birth 這3個屬性,並且,被賦予不同的屬性值,訪問屬性使用.操作符:
print xiaoming.name # 輸出 'Xiao Ming' print xiaohong.birth # 輸出 '1992-2-2'
要特別注意的是,初學者定義__init__()方法常常忘記了 self 引數:
>>> class Person(object):
... def __init__(name, gender, birth):
... pass
...
>>> xiaoming = Person('Xiao Ming', 'Male', '1990-1-1')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: __init__() takes exactly 3 arguments (4 given)
這會導致建立失敗或執行不正常,因為第一個引數name被Python直譯器傳入了例項的引用,從而導致整個方法的呼叫引數位置全部沒有對上。
要定義關鍵字引數,使用 **kw;
除了可以直接使用self.name = 'xxx'設定一個屬性外,還可以通過 setattr(self, 'name', 'xxx') 設定屬性。 class Person(object): def __init__(self, name, gender, birth, **kw): self.name = name self.gender = gender self.birth = birth for k,v in kw.iteritems(): setattr(self,k,v) xiaoming = Person('Xiao Ming', 'Male', '1990-1-1', job='Student') print xiaoming.name訪問限制
我們可以給一個例項繫結很多屬性,如果有些屬性不希望被外部訪問到怎麼辦?
Python對屬性許可權的控制是通過屬性名來實現的,如果一個屬性由雙下劃線開頭(__),該屬性就無法被外部訪問。看例子:
class Person(object): def __init__(self, name): self.name = name self._title = 'Mr' self.__job = 'Student' p = Person('Bob') print p.name # => Bob print p._title # => Mr print p.__job # => Error Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: 'Person' object has no attribute '__job'可見,只有以雙下劃線開頭的"__job"不能直接被外部訪問。
但是,如果一個屬性以"__xxx__"的形式定義,那它又可以被外部訪問了,以"__xxx__"定義的屬性在Python的類中被稱為特殊屬性,有很多預定義的特殊屬性可以使用,通常我們不要把普通屬性用"__xxx__"定義。
以單下劃線開頭的屬性"_xxx"雖然也可以被外部訪問,但是,按照習慣,他們不應該被外部訪問。 class Person(object): count = 0 def __init__(self,name): self.name = name Person.count = Person.count + 1 p1 = Person('Bob') print Person.count p2 = Person('Alice') print Person.count p3 = Person('Tim') print Person.count方法也是屬性
我們在 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() 才是方法呼叫。
定義類方法
和屬性類似,方法也分例項方法和類方法。
在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。
因為是在類上呼叫,而非例項上呼叫,因此類方法無法獲得任何例項變數,只能獲得類的引用。 class Person(object): __count = 0 def __init__(self, name): self.name = name Person.__count = Person.__count + 1 @classmethod def how_many(cls): return cls.__count print Person.how_many() p1 = Person('Bob') print Person.how_many() python的繼承:總是從某個類繼承,不要忘記呼叫super().__init__ def __init__(self, args): super(SubClass, self).__init__(args) pass繼承一個類
如果已經定義了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__()中將隱式傳遞,不需要寫出(也不能寫)。判斷型別
函式isinstance()可以判斷一個變數的型別,既可以用在Python內建的資料型別如str、list、dict,也可以用在我們自定義的類,它們本質上都是資料型別。
假設有如下的 Person、Student 和 Teacher 的定義及繼承關係如下:
class Person(object): def __init__(self, name, gender): self.name = name self.gender = gender class Student(Person): def __init__(self, name, gender, score): super(Student, self).__init__(name, gender) self.score = score class Teacher(Person): def __init__(self, name, gender, course): super(Teacher, self).__init__(name, gender) self.course = course p = Person('Tim', 'Male') s = Student('Bob', 'Male', 88) t = Teacher('Alice', 'Female', 'English')
當我們拿到變數 p、s、t 時,可以使用 isinstance 判斷型別:
>>> isinstance(p, Person) True # p是Person型別 >>> isinstance(p, Student) False # p不是Student型別 >>> isinstance(p, Teacher) False # p不是Teacher型別
這說明在繼承鏈上,一個父類的例項不能是子類型別,因為子類比父類多了一些屬性和方法。
我們再考察 s :
>>> isinstance(s, Person) True # s是Person型別 >>> isinstance(s, Student) True # s是Student型別 >>> isinstance(s, Teacher) False # s不是Teacher型別
s 是Student型別,不是Teacher型別,這很容易理解。但是,s 也是Person型別,因為Student繼承自Person,雖然它比Person多了一些屬性和方法,但是,把 s 看成Person的例項也是可以的。
這說明在一條繼承鏈上,一個例項可以看成它本身的型別,也可以看成它父類的型別。多型
類具有繼承關係,並且子類型別可以向上轉型看做父類型別,如果我們從 Person 派生出 Student和Teacher ,並都寫了一個 whoAmI() 方法:
class Person(object): def __init__(self, name, gender): self.name = name self.gender = gender def whoAmI(self): return 'I am a Person, my name is %s' % self.name class Student(Person): def __init__(self, name, gender, score): super(Student, self).__init__(name, gender) self.score = score def whoAmI(self): return 'I am a Student, my name is %s' % self.name class Teacher(Person): def __init__(self, name, gender, course): super(Teacher, self).__init__(name, gender) self.course = course def whoAmI(self): return 'I am a Teacher, my name is %s' % self.name
在一個函式中,如果我們接收一個變數 x,則無論該 x 是 Person、Student還是 Teacher,都可以正確打印出結果:
def who_am_i(x): print x.whoAmI() p = Person('Tim', 'Male') s = Student('Bob', 'Male', 88) t = Teacher('Alice', 'Female', 'English') who_am_i(p) who_am_i(s) who_am_i(t)
執行結果:
I am a Person, my name is Tim I am a Student, my name is Bob I am a Teacher, my name is Alice
這種行為稱為多型。也就是說,方法呼叫將作用在 x 的實際型別上。s 是Student型別,它實際上擁有自己的 whoAmI()方法以及從 Person繼承的 whoAmI方法,但呼叫 s.whoAmI()總是先查詢它自身的定義,如果沒有定義,則順著繼承鏈向上查詢,直到在某個父類中找到為止。
由於Python是動態語言,所以,傳遞給函式 who_am_i(x)的引數 x不一定是 Person 或 Person 的子型別。任何資料型別的例項都可以,只要它有一個whoAmI()的方法即可:
class Book(object): def whoAmI(self): return 'I am a book'這是動態語言和靜態語言(例如Java)最大的差別之一。動態語言呼叫例項方法,不檢查型別,只要方法存在,引數正確,就可以呼叫。
ython提供了open()函式來開啟一個磁碟檔案,並返回 File 物件。File物件有一個read()方法可以讀取檔案內容:
例如,從檔案讀取內容並解析為JSON結果:
import json f = open('/path/to/file.json', 'r') print json.load(f)由於Python的動態特性,json.load()並不一定要從一個File物件讀取內容。任何物件,只要有read()方法,就稱為File-like Object,都可以傳給json.load()。
多重繼承
除了從一個父類繼承外,Python允許從多個父類繼承,稱為多重繼承。
多重繼承的繼承鏈就不是一棵樹了,它像這樣:
class A(object): def __init__(self, a): print 'init A...' self.a = a class B(A): def __init__(self, a): super(B, self).__init__(a) print 'init B...' class C(A): def __init__(self, a): super(C, self).__init__(a) print 'init C...' class D(B, C): def __init__(self, a): super(D, self).__init__(a) print 'init D...'
像這樣,D 同時繼承自 B 和 C,也就是 D 擁有了 A、B、C的全部功能。多重繼承通過 super()呼叫__init__()方法時,A 雖然被繼承了兩次,但__init__()只調用一次:
>>> d = D('d') init A... init C... init B... init D...
多重繼承的目的是從兩種繼承樹中分別選擇並繼承出子類,以便組合功能使用。
舉個例子,Python的網路伺服器有TCPServer、UDPServer、UnixStreamServer、UnixDatagramServer,而伺服器執行模式有 多程序ForkingMixin 和 多執行緒ThreadingMixin兩種。
要建立多程序模式的 TCPServer:
class MyTCPServer(TCPServer, ForkingMixin) pass
要建立多執行緒模式的 UDPServer:
class MyUDPServer(UDPServer, ThreadingMixin): pass如果沒有多重繼承,要實現上述所有可能的組合需要 4x2=8 個子類。 正確的寫法: class C(A, B) def __init__(self, a, b): A.__init__(self, a) B.__init__(self, b) class Person(object): def __init__(self, name, gender, **kw): self.name = name self.gender = gender for k,w in kw.iteritems(): setattr(self,k,w) p = Person('Bob', 'Male', age=18, course='Python') print p.age print p.course python特殊方法 任何資料型別的例項都有__str__()這樣一個方法
__str__和__repr__
如果要把一個類的例項變成 str,就需要實現特殊方法__str__():
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)
現在,在互動式命令列下用 print 試試:
>>> p = Person('Bob', 'male') >>> print p (Person: Bob, male)
但是,如果直接敲變數 p:
>>> p <main.Person object at 0x10c941890>
似乎__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__
__cmp__
對 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。
Student類實現了按name進行排序:
>>> L = [Student('Tim', 99), Student('Bob', 88), Student('Alice', 77)] >>> print sorted(L) [(Alice: 77), (Bob: 88), (Tim: 99)]
注意: 如果list不僅僅包含 Student 類,則 __cmp__ 可能會報錯:
L = [Student('Tim', 99), Student('Bob', 88), 100, 'Hello'] print sorted(L)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.score > s.score: return -1 elif self.score < s.score: return 1 elif self.name > s.name: return 1 elif self.name < s.name: return -1 else: return 0 L = [Student('Tim', 99), Student('Bob', 88), Student('Alice', 99)] print sorted(L) 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.score == s.score:
return cmp(self.name, s.name)
return -cmp(self.score, s.score)
L = [Student('Tim', 99), Student('Bob', 88), Student('Alice', 99)]
print sorted(L)
class Fib(object): def __init__(self, num): a, b, L = 0, 1, [] for n in range(num): L.append(a) a, b = b, a + b self.numbers = L def __str__(self): return str(self.numbers) __repr__ = __str__ def __len__(self): return len(self.numbers) f = Fib(10) print f print len(f) gcd() #TBD class Fib(object): def __call__(self, num): a, b, L = 0, 1, [] for n in range(num): L.append(a) a, b = b, a + b return L f = Fib() print f(10) Next step: IO: 檔案和Socket; 多工: 程序和執行緒; 資料庫 Web開發 Ref: imooc 廖雪峰python進階篇教程