Python 從入門到進階之路(五)

以前的文章咱們簡單介紹了一下 Python 的函數,本篇文章咱們來看一下 Python 中的面向對象。編程

 Python從設計之初就已是一門面向對象的語言,正由於如此,在Python中建立一個類和對象是很容易的。數據結構

面向對象技術簡介

  • 類(Class): 用來描述具備相同的屬性和方法的對象的集合。它定義了該集合中每一個對象所共有的屬性和方法。對象是類的實例。
  • 方法:類中定義的函數。
  • 類變量:類變量在整個實例化的對象中是公用的。類變量定義在類中且在函數體以外。類變量一般不做爲實例變量使用。
  • 數據成員:類變量或者實例變量用於處理類及其實例對象的相關的數據。
  • 方法重寫:若是從父類繼承的方法不能知足子類的需求,能夠對其進行改寫,這個過程叫方法的覆蓋(override),也稱爲方法的重寫。
  • 局部變量:定義在方法中的變量,只做用於當前實例的類。
  • 實例變量:在類的聲明中,屬性是用變量來表示的。這種變量就稱爲實例變量,是在類聲明的內部可是在類的其餘成員方法以外聲明的。
  • 繼承:即一個派生類(derived class)繼承基類(base class)的字段和方法。繼承也容許把一個派生類的對象做爲一個基類對象對待。例如,有這樣一個設計:一個Dog類型的對象派生自Animal類,這是模擬"是一個(is-a)"關係(例圖,Dog是一個Animal)。
  • 實例化:建立一個類的實例,類的具體對象。
  • 對象:經過類定義的數據結構實例。對象包括兩個數據成員(類變量和實例變量)和方法。

和其它編程語言相比,Python 在儘量不增長新的語法和語義的狀況下加入了類機制。編程語言

Python中的類提供了面向對象編程的全部基本功能:類的繼承機制容許多個基類,派生類能夠覆蓋基類中的任何方法,方法中能夠調用基類中的同名方法。ide

對象能夠包含任意數量和類型的數據。函數

Python 中定義類的方法是以 class k開頭,咱們先定義一個類:ui

1 class Cat:
2     def eat(self):
3         print("貓吃魚")
4         
5 cat = Cat()
6 cat.eat()  # 貓吃魚

如上 ,咱們定義了一個 Cat 類,裏面定義了一個方法,咱們能夠經過 變量=類名() 的方式來實例化一個類,這樣咱們就能夠調用類裏面的屬性和方法,因此當咱們調用 cat.eat() 時輸出打印 Cat 類裏面的 eat() 方法。spa

在上面的 eat() 方法中,咱們默認穿了一個 self 的參數,它表明該類的實例,不必定非叫 self ,能夠叫 xxx,只是咱們約定俗成叫 self,跟 *args 和 **kwargs 一個道理,那該如何理解這個 self 呢,以下:設計

 1 class Cat:
 2     def eat(self):
 3         print(self.name+"吃魚")
 4 
 5 
 6 tom = Cat()
 7 tom.name = "Tom"
 8 tom.eat()  # Tom吃魚
 9 
10 jerry = Cat()
11 jerry.name = "Jerry"
12 jerry.eat()  # Jerry吃魚

在上面的代碼中,咱們定義了兩個 Cat 的實例,而且在每一個實例中都添加了一個 name 屬性,若是咱們想要在 eat() 方法中輸出各自定義的 name 值,咱們能夠經過 self 屬性來定義,由於 self 就表示該類的實例,因此就會去拿各自實例裏面的 name 值。code

上面的代碼中咱們能夠實現輸出不一樣的 name 值,可是須要咱們在實例化後本身定義 name,咱們也能夠在定義類的時候就將 name 值傳入來作:對象

 1 class Cat:
 2     def __init__(self,name):
 3         self.name = name
 4     def eat(self):
 5         print(self.name+"吃魚")
 6 
 7 tom = Cat("Tom")
 8 tom.eat()  # Tom吃魚
 9 
10 jerry = Cat("Jerry")
11 jerry.eat()  # Jerry吃魚

上面的代碼中,咱們在 Cat 類中定義了一個 __init__() 的方法,該方法是類自帶的方法,第一個參數必須爲 self,咱們能夠在裏面定義本身所需的變量。如上,咱們在實例化 Cat 類的時候就將 Tom ,Jeery 傳入,而後在 self 形參後面添加形參,該形參與傳入參數的順序意義對應,這樣咱們就可使用傳入的參數了,定義和使用時須要在前面加 self. 

在上面的代碼演示中,咱們能夠看出當咱們實例化一個類以後,就能調用該類的方法,這種方法叫公有方法,還有一種方法叫私有方法,就是實例化後不能被調用,只有本身內部能夠調用,以下:

 1 class Cat:
 2     def __init__(self,name):
 3         self.name= name
 4     def eat(self):
 5         print(self.name+"吃魚")
 6         self.__run()
 7     def __run(self):
 8         print("私有方法")
 9 
10 tom = Cat("Tom")
11 tom.eat()  # Tom吃魚  私有方法
12 tom.__run()  # AttributeError: 'Cat' object has no attribute '__run'

上面的代碼中當咱們實例化 tom 後,能夠經過 eat() 方法調用到 __run() 方法,可是直接調用 __run() 會報錯。

不只私有方法不能被調用,私有屬性也不能,以下:

 1 class Cat:
 2     age = 11
 3     __height = 120
 4     def add(self):
 5         self.age += 1
 6         self.__height += 1
 7 
 8 tom = Cat()
 9 tom.add()
10 print(tom.age)  # 12
11 print(tom.__height)  # AttributeError: 'Cat' object has no attribute '__height'

接下來咱們看一下類中的繼承,在上面的代碼中咱們定義了一個 Cat 的類,裏面有一個 eat() 方法,當咱們定義一個 Dog 類時,它也有一個 eat() 方法,並且 eat() 方法是同樣的,如今 Cat 類有一個 bark() 方法,Dog 類裏也有一個 bark() 方法,可是這兩個方法執行的結果不同,並且 Dog 類裏有一個 swim() 方法,而 Cat 裏沒有,若是咱們都分別定義這兩個類的話,代碼會很長,並且若是兩個相同的 eat() 方法須要修改時須要修改兩個地方,這時咱們能夠用繼承的方式解決,以下:

 1 class Animal:
 2     def eat(self):
 3         print("吃吃吃")
 4 
 5 class Cat(Animal):
 6     def bark(self):
 7         print("喵喵喵")
 8 
 9 class Dog(Animal):
10     def bark(self):
11         print("汪汪汪")
12 
13     def swim(self):
14         print("狗刨式")
15 
16 cat = Cat()
17 cat.eat()  # 吃吃吃
18 cat.bark()  # 喵喵喵
19 cat.swim()  # AttributeError: 'Cat' object has no attribute 'swim'
20 
21 dog = Dog()
22 dog.eat()  # 吃吃吃
23 dog.bark()  # 汪汪汪
24 dog.swim()  # 狗刨式

咱們將 Cat 類和 Dog 類相同的 eat() 方法定義在了 Animal 類裏,而後在建立 Cat 和 Dog 類時添加了 (Animal),意思是繼承 Animal 類,這樣咱們在實例化 Cat 和 Dog 類後就能調用 Animal 類裏的方法 eat(),並且還能調用各自實例裏的 bark() 方法,可是若是沒有另外一個類,則不能使用該類的方法,如 cat 調用 dog 的 swim() 方法就會報錯。

固然咱們可讓 Cat 類也繼承 Dog 類,以下:

 1 class Animal:
 2     def eat(self):
 3         print("吃吃吃")
 4 # Cat 類要想繼承 Dog 類必須寫在 Dog 類後面
 5 # class Cat(Dog,Animal):
 6 #     def bark(self):
 7 #         print("喵喵喵")
 8 
 9 class Dog(Animal):
10     def bark(self):
11         print("汪汪汪")
12     def swim(self):
13         print("狗刨式")
14 
15 class Cat(Dog,Animal):
16     def bark(self):
17         print("喵喵喵")
18 
19 cat = Cat()
20 cat.eat()  # 吃吃吃
21 cat.bark()  # 喵喵喵
22 cat.swim()  # 狗刨式
23 
24 dog = Dog()
25 dog.eat()  # 吃吃吃
26 dog.bark()  # 汪汪汪
27 dog.swim()  # 狗刨式

如上,咱們可讓 Cat 同時繼承 Dog 和 Animal 兩個類,可是如若想要繼承某類,必須先建立該類。

咱們也能夠多重繼承,即讓 Animal 類也繼承某類,這樣 Cat 和 Dog 若是繼承了 Animal 類,那麼也可使用 Animal 類繼承的父類的屬性和方法。

只得注意的是私有屬性和方法不能被繼承,由於私有屬性和方法只能在本身的類中使用。

 

接下來咱們看一下類中方法的重寫:

 1 class Animal:
 2     def eat(self):
 3         print("吃吃吃")
 4 
 5 class Cat(Animal):
 6     pass
 7 
 8 class Dog(Animal):
 9     def eat(self):
10         print("大口大口吃")
11 
12 cat = Cat()
13 cat.eat()  # 吃吃吃
14 
15 dog = Dog()
16 dog.eat()  # 大口大口吃

在上面的代碼中,咱們讓 Cat 和 Dog 類都繼承了 Animal 類,可是在 Dog 類中,咱們定義了一個和 Animal 類中同樣的方法名,可是執行的結果不同,當咱們分別調用實例化的 cat 和 dog 的 eat() 方法時,Cat 類因爲沒有本身的 eat() 方法,因此向上尋找,發現繼承的 Animal 類中有 eat() 方法,因此就調用了 Animal 類中的 eat() 方法,可是 Dog 類中有 eat() 方法,因此就調用本身的 eat() 方法,不在向上尋找,這就是類中方法的重寫。

 

在上面咱們說過了類屬性和實例方法,接下來咱們來看一下類方法:

 1 class Cat:
 2     # 類屬性
 3     age = 10
 4 
 5     # 實例方法
 6     def __init__(self):
 7         self.name = "Tom"
 8 
 9     def info(self):
10         print(self.name, self.age)
11 
12     # 類方法
13     @classmethod
14     def addAge(cls):
15         cls.age = 22
16 
17 tom = Cat()
18 tom.info()  # Tom 10
19 Cat.info()  # TypeError: info() missing 1 required positional argument: 'self'
20 
21 tom.addAge()
22 print(tom.age)  # 22
23 print(Cat.age)  # 22
24 
25 Cat.addAge()
26 print(tom.age)  # 22
27 print(Cat.age)  # 22

若是咱們在類裏面的方法前面加 @classmethod,就代表該方法爲類方法,在說類方法前咱們再來看一下實例方法。

當咱們實例化一個 tom 後,咱們就能夠調用 tom 裏面的實例方法,咱們以前說過 self 指的是該類的實例化,因此當咱們調用 tom.info() 時能正常調用,可是 Cat.info() 時則會報錯,由於裏面的 self 指向的是實例化的 tom,而不是 Cat 類,因此會報錯。

在類方法中,咱們一樣謠傳一個默認的形參,默認叫 cls,它指向的是類自己,而不是該類的實例化,因此咱們能夠經過 cls.age=22 來更改類裏面的類屬性,並且類方法 addAge() 可使用實例化的 tom 來調用,也可使用 Cat 類自己來調用。

相關文章
相關標籤/搜索