Python 簡明教程 --- 19,Python 類與物件

碼農充電站發表於2020-06-26

微信公眾號:碼農充電站pro
個人主頁:https://codeshellme.github.io

那些能用計算機迅速解決的問題,就別用手做了。
—— Tom Duff

目錄
在這裡插入圖片描述

上一節 我們介紹了Python 物件導向的相關概念,我們已經知道類與物件物件導向程式設計中非常重要的概念。

類就是一個模板,是抽象的。物件是由類建立出來的例項,是具體的。由同一個類建立出來的物件擁有相同的方法屬性,但屬性的值可以是不同的。不同的物件是不同的例項,互不干擾。

1,類的定義

如下,是一個最簡單的類,實際上是一個空類,不能做任何事情:

class People:
    pass

在Python 中定義一個類,需要用到class 關鍵字,後邊是類名,然後是一個冒號:,然後下一行是類中的程式碼,注意要有縮排

2,建立物件

People 雖然是一個空類,但依然可以建立物件,建立一個物件的語法為:

物件名 = 類名(引數列表)

引數列表是跟__init__ 構造方法相匹配的,如果沒有編寫__init__ 方法,建立物件時,就不需要寫引數,如下:

>>> p = People()
>>> p
<__main__.People object at 0x7fd30e60be80>
>>> 
>>> p1 = People()
>>> p1
<__main__.People object at 0x7fd30e60be48>

pp1 都是People類的物件。0x7fd30e60be80p 的地址,0x7fd30e60be48p1 的地址。可以看到不同的物件的地址是不同的,它們是兩不同的例項,互不干擾。

3,屬性

類中可以包含屬性類中的變數),建立出來的物件就會擁有相應的屬性,每個物件的屬性的值可以不同。

建立好物件後,可以用如下方法給物件新增屬性:

>>> p = People()
>>> p.name = '小明' # 新增 name 屬性
>>> p.sex = '男'    # 新增 sex 屬性
>>> p.name         # 訪問物件的屬性
'小明'
>>> p.sex          # 訪問物件的屬性
'男'

雖然在技術上可以這樣做,但是一般情況下,我們並不這樣為物件新增屬性,這樣會破壞類的封裝性,使得程式碼混亂,不利於維護。

當訪問一個不存在的屬性時,會出現異常:

>>> p.job         # 一個不存在的屬性
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'People' object has no attribute 'job'

我們一般會在__init__ 方法中為類新增屬性並賦值。

4,__init__ 方法

在Python 的類中,以雙下劃線__開頭和結尾的方法,被稱為魔法方法,每個魔法方法都有特定的含義。Python 為我們規定了一些魔法方法,讓我們自己實現這些方法。

__init__ 方法叫做構造方法,用來初始化物件。Python 直譯器會在生成物件時,自動執行構造方法,而無需使用者顯示呼叫。

__init__ 方法不需要有返回值。

類中的所有例項方法 方法,都至少有一個引數,就是self。Python 中的self 相當於C++ 和Java 中的this 指標,都是代表當前物件。只是Python 中的self 需要顯示寫在方法的第一個引數,而this 指標則不需要寫在方法引數中。

構造方法一般用於初始化物件的一些屬性,建構函式可以不寫,也可以只有一個self 引數。

當建構函式只有一個self 引數時,建立該類的物件時,不需要新增引數。當建構函式除了self 引數還有其它引數時,建立該類的物件時,則需要新增相匹配的引數。

比如,我們定義一個People 類,它有三個屬性,分別是namesexage

class People:

    def __init__(self, name, sex, age):
        self.name = name
        self.sex = sex
        self.age = age
        print('執行了 __init__ 方法')

    def print_info(self):
        print('people:%s sex:%s age:%s' % (
            self.name, self.sex, self.age))

在這個People 類中除了有一個__init__ 方法外,還有一個print_info 方法,每個方法中的都有self 引數,並且是第一個引數,self 代表當前物件。

在建立該類的物件時,需要傳遞匹配的引數(self 引數不用傳遞):

>>> p = People('小明', '男', 18)
執行了 __init__ 方法
>>> p 
<People.People object at 0x7feb6276bda0>
>>> p.print_info()
people:小明 sex:男 age:18
>>>
>>> p1 = People('小美', '女', 18)
執行了 __init__ 方法
>>> p1
<People.People object at 0x7fd54352be48>
>>> p1.print_info()
people:小美 sex:女 age:18

可以看到,在建立pp1 物件時,字串執行了 __init__ 方法 被列印了出來,而我們並沒有顯示呼叫該方法,說明__init__ 方法被預設執行了。

物件pp1 是兩個不同的物件,擁有相同的屬性和方法,但是屬性值是不一樣的。兩個物件互不干擾,物件p 的地址為0x7feb6276bda0p1 的地址是0x7fd54352be48

執行程式碼p.print_info(),是呼叫p 物件的print_info() 方法,因為,在定義該方法的時候,只有一個self 引數,所以在呼叫該方法的時候,不需要有引數。

5,私有屬性和方法

私有屬性

普通的屬性,就像上面的namesexage 屬性,都是公有屬性,在類的外部都可以被任意的訪問,就是可以用物件.屬性名的方式來訪問屬性,如下:

>>> p = People('小明', '男', 18)
執行了 __init__ 方法
>>> p.name  # 訪問屬性
'小明'
>>> p.name = '小麗'  # 修改屬性
>>> p.name  # 訪問屬性
'小麗'

這樣就破壞了資料的封裝性,這種訪問方式是不可控(會不受限制的被任意訪問)的,不利於程式碼的維護,不符合物件導向的程式設計規範。

所以,通常我們會將類中的屬性,改為私有屬性,就是不能以物件.屬性名 這樣的方式訪問類屬性。

在Python 中,通過在屬性名的前邊新增雙下劃線__,來將公有屬性變為私有屬性,如下:

#! /usr/bin/env python3

class People:

    def __init__(self, name, sex, age):
        self.__name = name   # 兩個下劃線
        self.__sex = sex     # 兩個下劃線
        self._age = age      # 一個下劃線
        print('執行了 __init__ 方法')

    def print_info(self):
        print('people:%s sex:%s age:%s' % (
            self.__name, self.__sex, self._age))

這樣就無法通過物件.屬性名的方式來訪問屬性了,如下:

>>> p = People('小美', '女', 18)
執行了 __init__ 方法
>>> p.__name        # 出現異常
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'People' object has no attribute '__name'

但是,Python 中這種私有屬性的方式,並不是真正的私有屬性,Python 只是將__name 轉換為了_People__name,即是在__name 的前邊加上了_類名(_People),我們依然可以這樣訪問__name 屬性:

>>> p._People__name
'小美'

但我們並不提倡這種方式,這會讓程式碼變得混亂難懂。

可以注意到,People 類中的_age 屬性是以單下劃線開頭的,這種以單下劃線開頭的屬性是可以在類的外部被訪問的:

>>> p._age
18

但是根據Python 規範,以單下劃線開頭的屬性,也被認為是私有屬性,也不應該在類的外部訪問(雖然在技術上是可以訪問的)。

注意:以雙下劃線__ 開頭且結尾的屬性__xxx__,是特殊屬性,是公有的,可在類的外部訪問

私有方法

私有方法與私有屬性類似,也可以在方法名的前邊加上雙下劃線__,來將某個方法變成私有的,一般不需要被外部訪問的方法,應該將其設定為私有方法

6,setget 方法

為了資料的封裝性,我們不應該直接在類的外部以物件.屬性名的方式訪問屬性,那麼如果我們需要訪問類的屬性該怎麼辦呢?

這時我們需要為每個私有屬性都提供兩個方法:

  • set 方法:用於設定屬性的值
  • get 方法:用於訪問屬性的值

為了減少程式碼量,這裡只為__name 屬性設定了這兩個方法,程式碼如下:

#! /usr/bin/env python3

class People:

    def __init__(self, name, sex, age):
        self.__name = name
        self.__sex = sex
        self._age = age
        print('執行了 __init__ 方法')

    def print_info(self):
        print('people:%s sex:%s age:%s' % (
            self.__name, self.__sex, self._age))

    # set 和 get 方法
    def set_name(self, name):
        self.__name = name

    def get_name(self):
        return self.__name

使用者可以這樣設定和訪問類的屬性:

>>> from People import People
>>> p = People('小美', '女', 18)
執行了 __init__ 方法
>>> p.get_name()         # 獲取 name 值
'小美'
>>> p.set_name('小麗')   # 設定新的值
>>> p.get_name()        # 再次獲取name 值
'小麗'

(完。)


推薦閱讀:

Python 簡明教程 --- 14,Python 資料結構進階

Python 簡明教程 --- 15,Python 函式

Python 簡明教程 --- 16,Python 高階函式

Python 簡明教程 --- 17,Python 模組與包

Python 簡明教程 --- 18,Python 物件導向


歡迎關注作者公眾號,獲取更多技術乾貨。

碼農充電站pro

相關文章