Python 基礎入門 - 5 面向對象基礎

Python 基礎入門前四篇:html

第五篇主要介紹 Python 的面向對象基礎知識,也就是類的介紹,包括類方法和屬性、構造方法、方法重寫、繼承等,最後給出兩道簡單的練習題。python


5.面向對象

5.1 簡介

先簡單介紹一些名詞概念。git

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

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

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

5.2 類定義

下面是簡單定義一個類:bash

# 定義一個動物類別
class Animal(object):
    # 類變量
    eat = True
    
    def __init__(self, name, gender):
        self.name = name
        self.gender = gender

     # 類方法
    def run(self):
        return 'Animal run!'
# 實例化類
anm = Animal('animal', 'male')

# 訪問類的屬性和方法
print("Animal 類的屬性 eat 爲:", anm.eat)
print("Animal 類的方法 run 輸出爲:", anm.run())
複製代碼

輸出結果:微信

Animal 類的屬性 eat 爲: True
Animal 類的方法 run 輸出爲: Animal run!
複製代碼

上述是一個簡單的類的定義,一般一個類須要有關鍵字 class ,而後接一個類的名字,而後若是是 python2.7 是須要如例子所示加上圓括號和 object ,但在 python3 版本中,其實能夠直接以下所示:數據結構

class Animal:
複製代碼
構造方法和特殊參數 self 的表示

而後 __init__構造方法,即在進行類實例化的時候會調用該方法,也就是 anm = Animal('animal', 'male')python2.7

此外,對於類的方法,第一個參數也是必須帶上的參數,按照慣例名稱是 self ,它表明的是類的實例,也就是指向實例自己的引用,讓實例自己能夠訪問類中的屬性和方法。以下代碼所示:機器學習

class Test:
    def prt(self):
        print(self)
        print(self.__class__)
 
t = Test()
t.prt()
複製代碼

輸出結果:

<__main__.Test object at 0x000002A262E2BA20>
<class '__main__.Test'>
複製代碼

能夠看到 print(self) 的結果是輸出當前對象的地址,而 self.__class__ 表示的就是類。

剛剛說了 self 只是慣例取的名稱,換成其餘名稱也能夠,以下所示:

# 不用 self 名稱
class Test2:
    def prt(sss):
        print(sss)
        print(sss.__class__)

t2 = Test2()
t2.prt()
複製代碼

輸出結果是同樣的,類實例的地址改變了而已。

<__main__.Test2 object at 0x000001FB7644BBA8>
<class '__main__.Test2'>
複製代碼
類方法

類方法和構造方法同樣,首先是關鍵字 def ,接着就是參數第一個必須是 self ,表示類實例的參數。

#類定義
class people:
    #定義基本屬性
    name = ''
    age = 0
    #定義私有屬性,私有屬性在類外部沒法直接進行訪問
    __weight = 0
    #定義構造方法
    def __init__(self,n,a,w):
        self.name = n
        self.age = a
        self.__weight = w
    def speak(self):
        print("%s 說: 我 %d 歲。" %(self.name,self.age))
 
# 實例化類
p = people('runoob',10,30)
p.speak()
複製代碼

輸出結果

runoob 說: 我 10 歲。
複製代碼

5.3 繼承

繼承的語法定義以下:

class DerivedClassName(BaseClassName1,BaseClassName2,...):
    <statement-1>
    .
    .
    .
    <statement-N>
複製代碼

須要注意:

  • 圓括號中基類的順序,當基類含有相同方法名,子類沒有指定(即相似 BaseClass1.method1()),python 會從左到右搜索繼承的基類是否包含該方法;
  • 基類和子類必須定義在一個做用域內

下面給出一個代碼例子,基類定義仍是上一節中的 people 類別,此次定義一個子類 student

# 單繼承示例
class student(people):
    grade = ''

    def __init__(self, n, a, w, g):
        # 調用父類的構造方法
        people.__init__(self, n, a, w)
        self.grade = g

    # 覆寫父類的方法
    def speak(self):
        print("%s 說: 我 %d 歲了,我在讀 %d 年級" % (self.name, self.age, self.grade))


s = student('ken', 10, 60, 3)
s.speak()
複製代碼

輸出結果

ken 說: 我 10 歲了,我在讀 3 年級
複製代碼

這是一個單繼承,即繼承一個基類的示例,子類的構造方法必須先調用基類(父類)的構造方法:

# 調用父類的構造方法
people.__init__(self, n, a, w)
複製代碼

另外一種調用基類的構造方法,利用 super() 函數:

super.__init__(self, n, a, w)
複製代碼
方法重寫

上述例子還重寫了基類的方法 speak()

方法重寫是在基類的方法沒法知足子類的需求時,在子類重寫父類的方法。

多繼承

python 也支持多繼承,下面是一個例子,繼續沿用剛剛定義的一個類 student ,而後再從新定義一個基類 speaker

#另外一個類,多重繼承以前的準備
class speaker():
    topic = ''
    name = ''
    def __init__(self,n,t):
        self.name = n
        self.topic = t
    def speak(self):
        print("我叫 %s,我是一個演說家,我演講的主題是 %s"%(self.name,self.topic))
 
#多重繼承
class sample(speaker,student):
    a =''
    def __init__(self,n,a,w,g,t):
        student.__init__(self,n,a,w,g)
        speaker.__init__(self,n,t)
 
test = sample("Tim",25,80,4,"Python")
test.speak()   #方法名同,默認調用的是在括號中排前地父類的方法
複製代碼

輸出結果:

我叫 Tim,我是一個演說家,我演講的主題是 Python
複製代碼

而若是想指定任意父類的方法,能夠添加下面這段代碼:

# 顯示調用 student 父類的 speak 方法
def speak(self):
    super(student, self).speak()
複製代碼

上面介紹過了, super() 函數是調用父類的一個方法,能夠直接 super().method() ,但若是是多繼承而且指定父類的話,就如上述所示,添加父類名字以及 self 來表示類實例。

另外,python2.7 調用 super() 方法,也須要傳入父類名字和 self 兩個參數。

5.4 類屬性與方法

屬性和方法的訪問權限,便可見性,有三種,公開、受保護以及私有,私有方法和私有屬性以下定義:

  • 類的私有屬性:兩個下劃線開頭,聲明該屬性私有,不能在類的外部被使用或直接訪問,而在類內部的方法中使用時:self.__private_attrs

  • 類的私有方法:兩個下劃線開頭,聲明該方法爲私有方法,只能在類的內部調用 ,不能在類的外部調用。self.__private_methods

而若是是受保護的屬性或者方法,則是一個下劃線開頭,例如 _protected_attr

下面是一個簡單的示例:

class JustCounter:
    __secretCount = 0  # 私有變量
    publicCount = 0  # 公開變量

    def count(self):
        self.__secretCount += 1
        self.publicCount += 1
        print(self.__secretCount)
        self.__count()

    def __count(self):
        print('私有方法')


counter = JustCounter()
counter.count()
counter.count()
print(counter.publicCount)
print(counter.__secretCount)  # 報錯,實例不能訪問私有變量
print(counter.__count())
複製代碼

輸出結果

1
私有方法
2
私有方法
2
複製代碼

調用私有屬性會報錯:

AttributeError: 'JustCounter' object has no attribute '__secretCount'
複製代碼

調用私有方法會報錯:

AttributeError: 'JustCounter' object has no attribute '__count'
複製代碼

類的屬性不只能夠是變量,也能夠是類實例做爲一個屬性,例子以下所示:

class TimeCounter:
    def __init__(self):
        print('timer')


class JustCounter:
    __secretCount = 0  # 私有變量
    publicCount = 0  # 公開變量

    def __init__(self):
        self.timer = TimeCounter()

    def count(self):
        self.__secretCount += 1
        self.publicCount += 1
        print(self.__secretCount)
        self.__count()

    def __count(self):
        print('私有方法')


counter = JustCounter()
counter.count()
counter.count()
print(counter.publicCount)
複製代碼

一樣繼續採用 JustCounter 類,只是新定義 TimeCounter ,並在 JustCounter 調用構造方法,實例化一個 TimeCounter 類,輸出結果:

timer
1
私有方法
2
私有方法
2
複製代碼

5.5 練習

最後是來自 Python-100-Days--Day08面向對象基礎 的兩道練習題:

定義一個簡單的數字時鐘

這個例子將採用受保護的屬性,即屬性名字以單下劃線開頭,因此初始化的構造方法以下:

from time import sleep


class Clock(object):
    """數字時鐘"""

    def __init__(self, hour=0, minute=0, second=0):
        ''' 初始化三個基本屬性,時,分,秒 :param hour: :param minute: :param second: '''
        self._hour = hour
        self._minute = minute
        self._second = second
複製代碼

而後是模擬時鐘的運行,這裏只須要注意時鐘運行過程邊界問題,即秒和分都是每到 60 須要置零,並讓分或者時加 1,而時是每隔 24 須要進行這樣的操做

def run(self):
     ''' 模擬時鐘的運行 :return: '''
     self._second += 1
     if self._second == 60:
     	self._second = 0
     	self._minute += 1
     	if self._minute == 60:
     		self._minute = 0
     		self._hour += 1
    		 if self._hour == 24:
     			self._hour = 0
複製代碼

最後是顯示時間,須要注意時、分和秒三個屬性都是整數,若是採用 % 進行格式化,須要調用 str() 方法顯示將它們從整數變成字符串類型,而若是用 format() 方法,就不須要。

def show(self):
    ''' 顯示時間 :return: '''
    print("{:02d}:{:02d}:{:02d}".format(self._hour, self._minute, self._second))
複製代碼

簡單的運用例子,這裏調用 time.sleep() 方法,每顯示一次時間休眠一秒,而後運行,設置循環次數是 5 次。

# 簡單時鐘例子
clock = Clock(23, 59, 57)
i = 0
while i < 5:
    clock.show()
    sleep(1)
    clock.run()
    i += 1
複製代碼

輸出結果:

23:59:57
23:59:58
23:59:59
00:00:00
00:00:01
複製代碼
定義一個類描述點之間的移動和距離

第二個練習是定義一個類,描述平面上點之間的移動和距離計算

首先是基本的構造方法定義,這裏做爲平面上的點,須要定義的屬性就是點的橫縱座標:

# 定義描述平面上點之間的移動和計算距離的類
class Point(object):
    def __init__(self, x=0, y=0):
        ''' 初始的座標 :param x:橫座標 :param y:縱座標 '''
        self._x = x
        self._y = y
複製代碼

接着,點的移動,能夠有兩種實現,第一種直接說明目標點的座標:

def move_to(self, new_x, new_y):
    ''' 移動到新的座標 :param new_x:新的橫座標 :param new_y:新的縱座標 :return: '''
    self._x = new_x
    self._y = new_y
複製代碼

第二種則是隻告訴分別在橫、縱兩個方向移動的距離:

def move_by(self, dx, dy):
    ''' 移動指定的增量 :param dx:橫座標的增量 :param dy:縱座標的增量 :return: '''
    self._x += dx
    self._y += dy
複製代碼

而後計算點之間的距離方法,這裏就須要用到剛剛從 math 庫導入的方法 sqrt ,即求取平方根:

def distance(self, other):
    ''' 計算與另外一個點的距離 :param other: :return: '''
    x_dist = self._x - other._x
    y_dist = self._y - other._y
    return sqrt(x_dist ** 2 + y_dist ** 2)
複製代碼

最後固然就是打印當前點的座標信息了:

def __str__(self):
     ''' 顯示當前點座標 :return: '''
     return '({},{})'.format(self._x, self._y)
複製代碼

簡單的應用例子

p1 = Point(10, 20)
p2 = Point(30, 5)
print('point1:', p1)
print('point2:', p2)
p1.move_to(15, 25)
print('after move to (15, 25), point1:', p1)
p1.move_by(20, 10)
print('move by (20, 10), point1:', p1)
dist = p1.distance(p2)
print('distance between p1 and p2: ', dist)
複製代碼

輸出結果:

point1: (10,20)
point2: (30,5)
after move to (15, 25), point1: (15,25)
move by (20, 10), point1: (35,35)
distance between p1 and p2:  30.4138126514911
複製代碼

參考:


小結

本文簡單介紹 Python 面向對象的基礎內容,主要是類的定義、方法和屬性介紹,繼承和方法重寫,這些是比較基礎的內容,後續計劃的進階內容關於面向對象部分,還會繼續介紹如多態、裝飾器等內容。

此外,本文的代碼都上傳到個人 github 上了:

github.com/ccc013/Pyth…

歡迎關注個人微信公衆號--機器學習與計算機視覺,或者掃描下方的二維碼,你們一塊兒交流,學習和進步!

相關文章
相關標籤/搜索