Python 簡明教程 --- 19,Python 類與物件
阿新 • • 發佈:2020-06-26
> **微信公眾號:碼農充電站pro**
> **個人主頁:**
> **那些能用計算機迅速解決的問題,就別用手做了。**
> —— Tom Duff
**目錄**
![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20200625172328301.png?#pic_center)
[上一節](https://www.cnblogs.com/codeshell/p/13193851.html) 我們介紹了`Python 面向物件`的相關概念,我們已經知道`類與物件`是`面向物件程式設計`中非常重要的概念。
類就是一個`模板`,是抽象的。物件是由類創建出來的例項,是具體的。由同一個類創建出來的物件擁有相同的`方法`和`屬性`,但屬性的值可以是不同的。不同的物件是不同的例項,互不干擾。
### 1,類的定義
如下,是一個最簡單的類,實際上是一個`空類`,不能做任何事情:
```python
class People:
pass
```
在Python 中定義一個類,需要用到`class` 關鍵字,後邊是`類名`,然後是一個`冒號:`,然後下一行是類中的程式碼,注意要有`縮排`。
### 2,建立物件
`People` 雖然是一個`空類`,但依然可以建立物件,建立一個物件的語法為:
```python
物件名 = 類名(引數列表)
```
引數列表是跟`__init__` 構造方法相匹配的,如果沒有編寫`__init__` 方法,建立物件時,就不需要寫引數,如下:
```shell
> >> p = People()
>>> p
<__main__.People object at 0x7fd30e60be80>
>>>
>>> p1 = People()
>>> p1
<__main__.People object at 0x7fd30e60be48>
```
`p` 和 `p1` 都是`People`類的物件。`0x7fd30e60be80` 是`p` 的地址,`0x7fd30e60be48` 是`p1` 的地址。可以看到不同的物件的地址是不同的,它們是兩不同的例項,互不干擾。
### 3,屬性
類中可以包含`屬性`(`類中的變數`),創建出來的物件就會擁有相應的屬性,每個物件的屬性的值可以不同。
建立好物件後,可以用如下方法給物件新增屬性:
```shell
>>> p = People()
>>> p.name = '小明' # 新增 name 屬性
>>> p.sex = '男' # 新增 sex 屬性
>>> p.name # 訪問物件的屬性
'小明'
>>> p.sex # 訪問物件的屬性
'男'
```
雖然在技術上可以這樣做,但是一般情況下,我們並不這樣為物件新增屬性,這樣會破壞類的`封裝性`,使得程式碼混亂,不利於維護。
當訪問一個不存在的屬性時,會出現異常:
```shell
>>> p.job # 一個不存在的屬性
Traceback (most recent call last):
File "", line 1, in
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` 類,它有三個屬性,分別是`name`,`sex`,`age`:
```python
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` 引數不用傳遞):
```shell
>>> p = People('小明', '男', 18)
執行了 __init__ 方法
>>> p
>>> p.print_info()
people:小明 sex:男 age:18
>>>
>>> p1 = People('小美', '女', 18)
執行了 __init__ 方法
>>> p1
>>> p1.print_info()
people:小美 sex:女 age:18
```
可以看到,在建立`p` 和`p1` 物件時,字串`執行了 __init__ 方法` 被列印了出來,而我們並沒有顯示呼叫該方法,說明`__init__` 方法被預設執行了。
物件`p` 和`p1` 是兩個不同的物件,擁有相同的屬性和方法,但是屬性值是不一樣的。兩個物件互不干擾,物件`p` 的地址為`0x7feb6276bda0`,`p1` 的地址是`0x7fd54352be48`。
執行程式碼`p.print_info()`,是呼叫`p` 物件的`print_info()` 方法,因為,在定義該方法的時候,只有一個`self` 引數,所以在呼叫該方法的時候,不需要有引數。
### 5,私有屬性和方法
**私有屬性**
普通的屬性,就像上面的`name`,`sex`和`age` 屬性,都是`公有屬性`,在類的外部都可以被任意的訪問,就是可以用`物件.屬性名`的方式來訪問屬性,如下:
```shell
>>> p = People('小明', '男', 18)
執行了 __init__ 方法
>>> p.name # 訪問屬性
'小明'
>>> p.name = '小麗' # 修改屬性
>>> p.name # 訪問屬性
'小麗'
```
這樣就破壞了資料的`封裝性`,這種訪問方式是不可控(會不受限制的被任意訪問)的,不利於程式碼的維護,不符合面向物件的程式設計規範。
所以,通常我們會將類中的屬性,改為`私有屬性`,就是不能以`物件.屬性名` 這樣的方式訪問類屬性。
在Python 中,通過在屬性名的前邊新增雙下劃線`__`,來將`公有屬性`變為`私有屬性`,如下:
```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))
```
這樣就無法通過`物件.屬性名`的方式來訪問屬性了,如下:
```shell
>>> p = People('小美', '女', 18)
執行了 __init__ 方法
>>> p.__name # 出現異常
Traceback (most recent call last):
File "", line 1, in
AttributeError: 'People' object has no attribute '__name'
```
但是,Python 中這種`私有屬性`的方式,並不是真正的私有屬性,Python 只是將`__name` 轉換為了`_People__name`,即是在`__name` 的前邊加上了`_類名`(`_People`),我們依然可以這樣訪問`__name` 屬性:
```shell
>>> p._People__name
'小美'
```
但我們並不提倡這種方式,這會讓程式碼變得混亂難懂。
可以注意到,`People` 類中的`_age` 屬性是以單下劃線開頭的,這種以單下劃線開頭的屬性是可以在類的外部被訪問的:
```shell
>>> p._age
18
```
但是根據Python 規範,以單下劃線開頭的屬性,也被認為是`私有屬性`,也不應該在類的外部訪問(雖然在技術上是可以訪問的)。
> 注意:以雙下劃線`__` 開頭且結尾的屬性`__xxx__`,是`特殊屬性`,是公有的,可在類的外部訪問
**私有方法**
私有方法與私有屬性類似,也可以在方法名的前邊加上雙下劃線`__`,來將某個方法變成私有的,一般不需要被外部訪問的方法,應該將其設定為`私有方法`。
### 6,`set` 和 `get` 方法
為了資料的`封裝性`,我們不應該直接在類的外部以`物件.屬性名`的方式訪問屬性,那麼如果我們需要訪問類的屬性該怎麼辦呢?
這時我們需要為每個私有屬性都提供兩個方法:
- set 方法:用於設定屬性的值
- get 方法:用於訪問屬性的值
為了減少程式碼量,這裡只為`__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))
# set 和 get 方法
def set_name(self, name):
self.__name = name
def get_name(self):
return self.__name
```
使用者可以這樣設定和訪問類的屬性:
```shell
>>> from People import People
>>> p = People('小美', '女', 18)
執行了 __init__ 方法
>>> p.get_name() # 獲取 name 值
'小美'
>>> p.set_name('小麗') # 設定新的值
>>> p.get_name() # 再次獲取name 值
'小麗'
```
(完。)
---
**推薦閱讀:**
[Python 簡明教程 --- 14,Python 資料結構進階](https://www.cnblogs.com/codeshell/p/13040449.html)
[Python 簡明教程 --- 15,Python 函式](https://www.cnblogs.com/codeshell/p/13040458.html)
[Python 簡明教程 --- 16,Python 高階函式](https://www.cnblogs.com/codeshell/p/13158903.html)
[Python 簡明教程 --- 17,Python 模組與包](https://www.cnblogs.com/codeshell/p/13158924.html)
[Python 簡明教程 --- 18,Python 面向物件](https://www.cnblogs.com/codeshell/p/13193851.html)
---
歡迎關注作者公眾號,獲取更多技術乾貨。
![碼農充電站pro](https://img-blog.csdnimg.cn/20200505082843773.png?#pic