目錄html
想學爬蟲仍是 python 專業啊,以前一直在用 java, 如今決定嚐嚐鮮,使用 python及爬蟲框架來完成網絡數據採集。
編程語言之間都是相通的,好比都須要模塊化,引入其餘文件來實現功能,使用列表等容器來處理數據,都要使用 json
或 xml
來解析和傳輸數據。
你會發現 經過類比的方式,帶着問題去學習,會走的很快
並且我認爲 代碼示例的做用是異常強大的, 我會盡可能使用代碼示例的方式來展現,以知足同窗快速學習的須要,也備後續查詢。java
在上篇文章 中,討論了 python 3 中 string, 數據結構(Dict, List, 元組)等重要的主題。
今天我會繼續探險,去征服 python 3 中的面向對象, let's go 讓咱們出發吧!python
剛接觸 python 中的類和對象,我也和大多數小夥伴同樣迷茫,不知道它和我所熟知的 java 都有什麼異同點,爲此我還提出了一大堆問題編程
class
的繼承?下面就一一來探索這些疑惑。json
在 java 中要建立一個類就必需要使用 class
關鍵字,要將類實例化,建立一個對象,可使用 new
關鍵字。在 python 中是怎麼樣的呢?網絡
先看代碼數據結構
class Person(): """這個叫作定義體,用來解釋類的用途""" print(Person) # <class '__main__.Person'> # 因爲是在程序頂層定義的,它的全名就是 '__main__.Person' person = Person() print(person) # <__main__.Person object at 0x000000000219A1D0>
要定義一個類(class) 只要將 class
關鍵字放在前面便可,類內部也能夠像 java 似的定義變量和函數,這個後面再看。框架
實例化一個類,也就是建立一個對象,並不須要使用 new
關鍵字,只需將 class
當作函數來調用就能夠啦,是否是比 java 簡潔很多。編程語言
瞭解了定義和實例化類,還有兩個問題:模塊化
isinstance
print(isinstance (person, Person)) # True
type
print(type(person)) # <class '__main__.Person'>
上面的代碼,光有一個空對象是幹不了任何事情的,咱們也要像 java 同樣爲其定義屬性和方法。
java 是不能動態定義一個變量的,必需要把它放在 class
中預先定義好才能夠用;而在 python 中這卻不是問題,不信看代碼~
class Person(): """這個叫作定義體,用來解釋類的用途""" person = Person() person.age = 5 print(person.age)
雖然在對 Person class
定義時沒有任何屬性的聲明,但在實例化後依然能夠添加 age 屬性,並且也並無看到如 java 中 public
, private
等訪問修飾符的存在, python 中有沒有這些概念呢?還真有,變量默認就是 public
公有的,若是 在變量名前添加兩個下劃線,這樣就會認爲是 private
私有變量了,直接訪問是不能夠的。看下面代碼
class Person(): """這個叫作定義體,用來解釋類的用途""" gender = 'male' __age = 5 person = Person() print(person.gender) # male print(person.__age) # AttributeError: 'Person' object has no attribute '__age'
上面代碼中,在打印 __age
時會報錯,告知沒有找到這個屬性,其實就是 因爲使用雙下劃線作前綴使其變成私有變量了。
那 函數名是否是也有私有函數,是否是也在前面加雙下劃線呢 ?猜的沒錯,這個咱們後面再瞭解。
既然 python 對象的屬性操做如此靈活,能夠動態添加,那用戶在使用時就可能會碰到一些異常。
比較典型的就是,訪問一個不存在的屬性,會拋出 AttributeError
。對這種狀況有兩種方式能夠處理:
hasattr
斷定對象是否擁有該屬性(記住,只對公有變量有效哦~)try
語句處理class Person(): """這個叫作定義體,用來解釋類的用途""" gender = 'male' __age = 5 person = Person() print(hasattr(person, 'gender')) # True print(hasattr(person, 'name')) # False print(hasattr(person, '__age')) # False try: name = person.name except AttributeError: name = 'unknown' print(name)
什麼是方法?方法和函數有什麼區別?在上一篇我就介紹了好多 string
的方法,爲何叫作方法,而不叫作 string
的函數呢?一塊兒來了解下~
調用方法分爲兩種形式,分別是
先看第一種函數調用語法,這其實和 java 中的靜態方法是同樣的,只是前面不須要 static
關鍵字。
class Person: def print_person(person): print('name: %s, gender%s, age:%d' % (person.name, person.gender, person.age)) person = Person() person.name = 'Tom' person.gender = 'male' person.age = 10 Person.print_person(person)
函數調用語法的方式其實和單純的函數調用,區別是不大的,由於方法前面的 class
對它沒起什麼做用,活動主體 依然是方法。
再看另一種 方法調用語法,而此次的主體則是調用該方法的 對象
class Person: __name = 'Tom' __gender = 'male' __age = 10 def print_person(self): print('name: %s, gender:%s, age:%d' % (self.__name, self.__gender, self.__age)) person = Person() person.print_person()
細心的同窗會發現這裏在定義方法時形參爲 self
, 而在調用方法時卻沒有任何入參。
那這個 self
是什麼呢?
若是類比 java 的話,這個 self
能夠看做是 this
, 其實就是對當前對象的引用。 java 中定義方法時沒必要將其作入參。而這個 self
在 python 中則是必須聲明的,在調用的時候則沒必要傳入。
注意,這個 self
可不是關鍵字哦,只要佔據方法形參的頭把交椅,你能夠用任何名字。
在 java 中構造函數是與類同名的,並且會伴隨着實例化的動做而執行。在 python 中呢?
python 中的構造函數叫作 init
方法,全名是 __init__
具體看下面代碼
class Person(): __gender = 'male' __age = '0' def __init__(self, gender='male', age=0): self.__gender = gender self.__age = age person1 = Person('female', 10) person2 = Person() person2 = Person('male')
做爲實例方法, self
入參固然少不了,其餘參數就按照順序排開,若參數不夠,就用默認值來代替。
在java 中, 咱們通常會覆蓋 toString() 方法來返回對象中包含的值得關注的信息。 python 中也有這樣一個方法,叫作 __str__
。
class Person: __name = 'Tom' __gender = 'male' __age = 10 def __str__(self): return ('name: %s, gender:%s, age:%d' % (self.__name, self.__gender, self.__age)) person = Person() print(person)
做爲最佳實踐的一部分,建議你在每一個建立的類中都覆蓋這個方法。
還記得面向對象的幾個特徵嗎?封裝性,繼承性,多態性。嗯,來聊下 python 對多態的實現。
什麼叫作多態?
在 java 中,若是在一個 class 中有多個函數,函數名相同而參數不一樣(個數或類型不一樣),就叫作多態。
而在 python 中, 多態的概念則更進一步,對於同一個函數,若是可以處理多種類型的數據,也叫作多態。
tuple_list = [(1, 2,), (2, 3,), (4, 5)] list = [1, 2, 3, 4] dict1 = { 'a' : 1, 'b' : 2 } def printSomething(something): for i in something: print(i) print(tuple_list) print(dict1) print(list)
printSomething
一個函數能夠同時打印元組,列表以及字典,充分發揮代碼複用的功效,是否是很方便。
聊完了多態,再來看看面向對象的另外一個特徵:繼承性。
什麼是繼承?繼承就是定義好了一個類 A(父類);再定義一個新類 B(子類),類 B 擁有類 A 的方法和屬性,而且又定義了新的屬性和方法。類 A 稱爲父類,類 B 稱爲子類。
java 中定義兩個類的繼承關係,使用 extends
關鍵字實現,在 python 中呢?
class Father: """ 這是一個父類 """ __age = 45 class Son(Father): """ 這是一個子類 """
python 中不須要加關鍵字來講明繼承關係,只須要將父類的名稱放在括號中就能夠了,看起來要比 java
簡潔一些。
前面講過, python class 中能夠定義本身的初始化函數,在實例化的時會被調用。那若是父類和子類都有初始化函數或者父類有而子類沒有,那初始化函數該如何執行呢?這裏分爲三種狀況來講明,先來看第一種。
父類有 init 而子類沒有, 這時父類的初始化函數會被默認調用
class Father(): """ 這是一個父類 """ def __init__(self, age): print("Father's init function invoke") self.__age = age class Son(Father): """ 這是一個子類 """ son = Son(5)
這裏要注意,父類中須要的 age
參數必定要傳進去哦,要否則會報錯的。
父類,子類都有 init ,而子類沒有顯式調用父類的 init 方法時,父類初始化函數是不會被調用的
class Father(): """ 這是一個父類 """ def __init__(self, age): print("Father's init function invoke") self.__age = age def get_age(self): return self.__age class Son(Father): """ 這是一個子類 """ def __init__(self, age): print("Son's init function invoke") self.__age = age son = Son(5) # Son's init function invoke print(son.get_age()) # AttributeError: 'Son' object has no attribute '_Father__age'
細心的同窗會發現,代碼中的最後一句報錯了,表示 Son 對象沒有 Father
類的 __age
變量。這是由於
__age
變量則沒有初始化get_age
函數是被子類從父類繼承來的,返回的是父類的 __age
變量那我要是想解決這個錯誤,該怎麼作呢?有兩種方法
Son
的初始化函數中顯式調用父類 Father
的初始化函數Son
中從新定義個 get_age
方法,這樣就會覆蓋父類的同名方法,返回的是子類的 _age
變量第二種方法就不貼代碼了,感興趣的話能夠試試。重點來看第一種方法,這就引出了第 3 種狀況。
子類在本身定義的 init 方法中,顯式調用父類的 init 方法,父類和子類的屬性都會被初始化
class Father(): """ 這是一個父類 """ def __init__(self, age): print("Father's init function invoke") self.__age = age def get_age(self): return self.__age class Son(Father): """ 這是一個子類 """ def __init__(self, age): print("Son's init function invoke") self.__age = age super(Son, self).__init__(age + 25) def get_age(self): return self.__age def get_father_age(self): return super(Son, self).get_age() son = Son(5) # Son's init function invoke # Father's init function invoke print(son.get_father_age()) # 30 print(son.get_age()) # 5
看到代碼中是怎麼調用父類的初始化函數嗎? 對,用的是 super
。
java 中也有 super
關鍵字,表示對父類的指代, python 的 super
是怎麼用的,原理是什麼?咱們來看下。
下面說明的只針對 python 單繼承的狀況,多繼承這裏暫不涉及,有興趣的同窗能夠自行充電。
在單繼承中,super
也能夠看作對其父類的指代,它的使用場合就是用來調用父類的方法:
__init__
方法它的寫法是 super(Son,self).xxx
, 固然也能夠寫成 super()
這種簡寫的形式。
來看代碼
class Father(): """ 這是一個父類 """ def __init__(self, age): print("Father's init function invoke") self.__age = age def get_age(self): return self.__age class Son(Father): """ 這是一個子類 """ def __init__(self, age): print("Son's init function invoke") self.__age = age super(Son, self).__init__(age + 25) def get_age(self): return self.__age def get_father_age(self): return super(Son, self).get_age() son = Son(5) # Son's init function invoke # Father's init function invoke print(son.get_father_age()) # 30 print(son.get_age()) # 5
經過代碼來窺探下它的執行原理,以 super(Son, self).get_age()
爲例
self
是 Son
的一個實例, super
把 self
轉化爲父類 Father
的一個實例對象self
通過了轉化, 那它獲得的 __age
, 也是父類初始化時獲得的 __age
看到這裏,不知您對 python 的面向對象有了多少理解,反正我是理解了很多,哈哈。若是有疑問和建議,歡迎留言交流,我將仔細閱讀,認真回覆。
下篇文章中會涉及到 文件, json xml 處理 處理等主題,敬請期待~