那些能用計(jì)算機(jī)迅速解決的問題陶舞,就別用手做了。
—— Tom Duff
目錄
上一節(jié) 我們介紹了Python 面向?qū)ο?/code>的相關(guān)概念撑帖,我們已經(jīng)知道
類與對象
是面向?qū)ο缶幊?/code>中非常重要的概念蓉坎。
類就是一個模板
,是抽象的胡嘿。對象是由類創(chuàng)建出來的實(shí)例蛉艾,是具體的。由同一個類創(chuàng)建出來的對象擁有相同的方法
和屬性
,但屬性的值可以是不同的伺通。不同的對象是不同的實(shí)例箍土,互不干擾。
1罐监,類的定義
如下吴藻,是一個最簡單的類,實(shí)際上是一個空類
弓柱,不能做任何事情:
class People:
pass
在Python 中定義一個類沟堡,需要用到class
關(guān)鍵字,后邊是類名
矢空,然后是一個冒號:
航罗,然后下一行是類中的代碼,注意要有縮進(jìn)
屁药。
2粥血,創(chuàng)建對象
People
雖然是一個空類
,但依然可以創(chuàng)建對象酿箭,創(chuàng)建一個對象的語法為:
對象名 = 類名(參數(shù)列表)
參數(shù)列表是跟__init__
構(gòu)造方法相匹配的复亏,如果沒有編寫__init__
方法,創(chuàng)建對象時缭嫡,就不需要寫參數(shù)缔御,如下:
>>> p = People()
>>> p
<__main__.People object at 0x7fd30e60be80>
>>>
>>> p1 = People()
>>> p1
<__main__.People object at 0x7fd30e60be48>
p
和 p1
都是People
類的對象。0x7fd30e60be80
是p
的地址妇蛀,0x7fd30e60be48
是p1
的地址耕突。可以看到不同的對象的地址是不同的评架,它們是兩不同的實(shí)例眷茁,互不干擾。
3古程,屬性
類中可以包含屬性
(類中的變量
)蔼卡,創(chuàng)建出來的對象就會擁有相應(yīng)的屬性,每個對象的屬性的值可以不同挣磨。
創(chuàng)建好對象后,可以用如下方法給對象添加屬性:
>>> p = People()
>>> p.name = '小明' # 添加 name 屬性
>>> p.sex = '男' # 添加 sex 屬性
>>> p.name # 訪問對象的屬性
'小明'
>>> p.sex # 訪問對象的屬性
'男'
雖然在技術(shù)上可以這樣做荤懂,但是一般情況下茁裙,我們并不這樣為對象添加屬性,這樣會破壞類的封裝性
节仿,使得代碼混亂晤锥,不利于維護(hù)。
當(dāng)訪問一個不存在的屬性時,會出現(xiàn)異常:
>>> p.job # 一個不存在的屬性
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'People' object has no attribute 'job'
我們一般會在__init__
方法中為類添加屬性并賦值矾瘾。
4女轿,__init__
方法
在Python 的類中,以雙下劃線__
開頭和結(jié)尾的方法壕翩,被稱為魔法方法
蛉迹,每個魔法方法都有特定的含義。Python 為我們規(guī)定了一些魔法方法放妈,讓我們自己實(shí)現(xiàn)這些方法北救。
__init__
方法叫做構(gòu)造方法
,用來初始化對象芜抒。Python 解釋器會在生成對象
時珍策,自動執(zhí)行構(gòu)造方法,而無需用戶顯示調(diào)用宅倒。
__init__
方法不需要有返回值攘宙。
類中的所有實(shí)例方法
方法,都至少有一個參數(shù)拐迁,就是self
蹭劈。Python 中的self
相當(dāng)于C++ 和Java 中的this
指針,都是代表當(dāng)前對象唠亚。只是Python 中的self
需要顯示寫在方法的第一個參數(shù)链方,而this
指針則不需要寫在方法參數(shù)中。
構(gòu)造方法一般用于初始化對象的一些屬性灶搜,構(gòu)造函數(shù)可以不寫祟蚀,也可以只有一個self
參數(shù)。
當(dāng)構(gòu)造函數(shù)只有一個self
參數(shù)時割卖,創(chuàng)建該類的對象時前酿,不需要添加參數(shù)。當(dāng)構(gòu)造函數(shù)除了self
參數(shù)還有其它參數(shù)時鹏溯,創(chuàng)建該類的對象時罢维,則需要添加相匹配的參數(shù)。
比如丙挽,我們定義一個People
類肺孵,它有三個屬性,分別是name
颜阐,sex
平窘,age
:
class People:
def __init__(self, name, sex, age):
self.name = name
self.sex = sex
self.age = age
print('執(zhí)行了 __init__ 方法')
def print_info(self):
print('people:%s sex:%s age:%s' % (
self.name, self.sex, self.age))
在這個People
類中除了有一個__init__
方法外,還有一個print_info
方法凳怨,每個方法中的都有self
參數(shù)瑰艘,并且是第一個參數(shù)是鬼,self
代表當(dāng)前對象。
在創(chuàng)建該類的對象時紫新,需要傳遞匹配的參數(shù)(self
參數(shù)不用傳遞):
>>> p = People('小明', '男', 18)
執(zhí)行了 __init__ 方法
>>> p
<People.People object at 0x7feb6276bda0>
>>> p.print_info()
people:小明 sex:男 age:18
>>>
>>> p1 = People('小美', '女', 18)
執(zhí)行了 __init__ 方法
>>> p1
<People.People object at 0x7fd54352be48>
>>> p1.print_info()
people:小美 sex:女 age:18
可以看到均蜜,在創(chuàng)建p
和p1
對象時,字符串執(zhí)行了 __init__ 方法
被打印了出來芒率,而我們并沒有顯示調(diào)用該方法囤耳,說明__init__
方法被默認(rèn)執(zhí)行了。
對象p
和p1
是兩個不同的對象敲董,擁有相同的屬性和方法紫皇,但是屬性值是不一樣的。兩個對象互不干擾腋寨,對象p
的地址為0x7feb6276bda0
聪铺,p1
的地址是0x7fd54352be48
。
執(zhí)行代碼p.print_info()
萄窜,是調(diào)用p
對象的print_info()
方法铃剔,因?yàn)椋诙x該方法的時候查刻,只有一個self
參數(shù)键兜,所以在調(diào)用該方法的時候,不需要有參數(shù)穗泵。
5普气,私有屬性和方法
私有屬性
普通的屬性,就像上面的name
佃延,sex
和age
屬性现诀,都是公有屬性
,在類的外部都可以被任意的訪問履肃,就是可以用對象.屬性名
的方式來訪問屬性仔沿,如下:
>>> p = People('小明', '男', 18)
執(zhí)行了 __init__ 方法
>>> p.name # 訪問屬性
'小明'
>>> p.name = '小麗' # 修改屬性
>>> p.name # 訪問屬性
'小麗'
這樣就破壞了數(shù)據(jù)的封裝性
,這種訪問方式是不可控(會不受限制的被任意訪問)的尺棋,不利于代碼的維護(hù)封锉,不符合面向?qū)ο蟮木幊桃?guī)范。
所以膘螟,通常我們會將類中的屬性成福,改為私有屬性
,就是不能以對象.屬性名
這樣的方式訪問類屬性荆残。
在Python 中闷叉,通過在屬性名的前邊添加雙下劃線__
,來將公有屬性
變?yōu)?code>私有屬性脊阴,如下:
#! /usr/bin/env python3
class People:
def __init__(self, name, sex, age):
self.__name = name # 兩個下劃線
self.__sex = sex # 兩個下劃線
self._age = age # 一個下劃線
print('執(zhí)行了 __init__ 方法')
def print_info(self):
print('people:%s sex:%s age:%s' % (
self.__name, self.__sex, self._age))
這樣就無法通過對象.屬性名
的方式來訪問屬性了,如下:
>>> p = People('小美', '女', 18)
執(zhí)行了 __init__ 方法
>>> p.__name # 出現(xiàn)異常
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'People' object has no attribute '__name'
但是,Python 中這種私有屬性
的方式嘿期,并不是真正的私有屬性品擎,Python 只是將__name
轉(zhuǎn)換為了_People__name
,即是在__name
的前邊加上了_類名
(_People
)备徐,我們依然可以這樣訪問__name
屬性:
>>> p._People__name
'小美'
但我們并不提倡這種方式萄传,這會讓代碼變得混亂難懂。
可以注意到蜜猾,People
類中的_age
屬性是以單下劃線開頭的秀菱,這種以單下劃線開頭的屬性是可以在類的外部被訪問的:
>>> p._age
18
但是根據(jù)Python 規(guī)范,以單下劃線開頭的屬性蹭睡,也被認(rèn)為是私有屬性
衍菱,也不應(yīng)該在類的外部訪問(雖然在技術(shù)上是可以訪問的)。
注意:以雙下劃線
__
開頭且結(jié)尾的屬性__xxx__
肩豁,是特殊屬性
脊串,是公有的,可在類的外部訪問
私有方法
私有方法與私有屬性類似清钥,也可以在方法名的前邊加上雙下劃線__
琼锋,來將某個方法變成私有的,一般不需要被外部訪問的方法祟昭,應(yīng)該將其設(shè)置為私有方法
缕坎。
6,set
和 get
方法
為了數(shù)據(jù)的封裝性
篡悟,我們不應(yīng)該直接在類的外部以對象.屬性名
的方式訪問屬性谜叹,那么如果我們需要訪問類的屬性該怎么辦呢?
這時我們需要為每個私有屬性都提供兩個方法:
- set 方法:用于設(shè)置屬性的值
- get 方法:用于訪問屬性的值
為了減少代碼量恰力,這里只為__name
屬性設(shè)置了這兩個方法叉谜,代碼如下:
#! /usr/bin/env python3
class People:
def __init__(self, name, sex, age):
self.__name = name
self.__sex = sex
self._age = age
print('執(zhí)行了 __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
用戶可以這樣設(shè)置和訪問類的屬性:
>>> from People import People
>>> p = People('小美', '女', 18)
執(zhí)行了 __init__ 方法
>>> p.get_name() # 獲取 name 值
'小美'
>>> p.set_name('小麗') # 設(shè)置新的值
>>> p.get_name() # 再次獲取name 值
'小麗'
(完。)
推薦閱讀:
Python 簡明教程 ---14踩萎,Python 數(shù)據(jù)結(jié)構(gòu)進(jìn)階
Python 簡明教程 ---15停局,Python 函數(shù)
Python 簡明教程 ---16,Python 高階函數(shù)
Python 簡明教程 ---17香府,Python 模塊與包
Python 簡明教程 ---18董栽,Python 面向?qū)ο?/a>