面向?qū)ο缶幊蹋?把一組數(shù)據(jù)結構和處理它們的方法組成對象(object),把相同行為的對象歸納為類(class),通過類的封裝(encapsulation)隱藏內(nèi)部細節(jié),通過繼承(inheritance)實現(xiàn)類的特化(specialization)和泛化(generalization),通過多態(tài)(polymorphism)實現(xiàn)基于對象類型的動態(tài)分派。
面向?qū)ο笏枷肴笠兀悍庋b,繼承和多態(tài)。
1? ?定義使用類??
class
Student(object):
#
__init__是一個特殊方法用于在創(chuàng)建對象時進行初始化操作
#
通過這個方法我們可以為學生對象綁定name和age兩個屬性
def
__init__
(self, name, age):
self.name
=
name
self.age
=
age
def
study(self, course_name):
print
(
'
%s正在學習%s.
'
%
(self.name, course_name))
#
PEP 8要求標識符的名字用全小寫多個單詞用下劃線連接
#
但是部分程序員和公司更傾向于使用駝峰命名法(駝峰標識)
def
watch_movie(self):
if
self.age < 18
:
print
(
'
%s只能觀看海賊王
'
%
self.name)
else
:
print
(
'
%s正在觀看動漫
'
%
self.name)
test
= Student(
"
非常君
"
, 18
)
test.study(
"
python
"
)
test.watch_movie()
2? 訪問可見性問題
在Python中,屬性和方法的訪問權限只有兩種,也就是公開的和私有的, Python 中將成員和方法私有化的方式是在成員名或者方法名前面加兩個下劃線 ,Python 中訪問私有成員變量的正確方式為:實例類. 類名_ 變量名? ?(不建議使用) 。
class
PrivateTest:
__name
=
"
private
"
if
__name__
==
"
__main__
"
:
pt
=
PrivateTest()
print
(pt._PrivateTest__name)
在實際開發(fā)中,我們并不建議將屬性設置為私有的,因為這會導致子類無法訪問(后面會講到)。所以大多數(shù)Python程序員會遵循一種命名慣例就是讓屬性名以單下劃線開頭來表示屬性是受保護的,本類之外的代碼在訪問這樣的屬性時應該要保持慎重。這種做法并不是語法上的規(guī)則,單下劃線開頭的屬性和方法外界仍然是可以訪問的,所以更多的時候它是一種暗示或隱喻。
Python內(nèi)置的
@property
裝飾器就是負責把一個方法變成屬性調(diào)用的。我們之前的建議是將屬性命名以單下劃線開頭,通過這種方式來暗示屬性是受保護的,不建議外界直接訪問,那么如果想訪問屬性可以通過屬性的getter(訪問器)和setter(修改器)方法進行對應的操作。如果要做到這點,就可以考慮使用@property包裝器來包裝getter和setter方法,使得對屬性的訪問既安全又方便
class
Person(object):
def
__init__
(self, name, age):
self._name
=
name
self._age
=
age
#
訪問器 - getter方法
@property
def
name(self):
return
self._name
#
訪問器 - getter方法
@property
def
age(self):
return
self._age
#
修改器 - setter方法
@age.setter
def
age(self, age):
self._age
=
age
def
play(self):
if
self._age <= 16
:
print
(
'
%s正在玩飛行棋.
'
%
self._name)
else
:
print
(
'
%s正在玩斗地主.
'
%
self._name)
def
main():
person
= Person(
'
王大錘
'
, 12
)
person.play()
person.age
= 22
person.play()
#
person.name = '白元芳' # AttributeError: can't set attribute
if
__name__
==
'
__main__
'
:
main()
__slots__魔法
我們講到這里,不知道大家是否已經(jīng)意識到,Python是一門動態(tài)語言。通常,動態(tài)語言允許我們在程序運行時給對象綁定新的屬性或方法,當然也可以對已經(jīng)綁定的屬性和方法進行解綁定。但是如果我們需要限定自定義類型的對象只能綁定某些屬性,可以通過在類中定義__slots__變量來進行限定。需要注意的是__slots__的限定只對當前類的對象生效,對子類并不起任何作用。
class
Person(object):
#
限定Person對象只能綁定_name, _age和_gender屬性
__slots__
= (
'
_name
'
,
'
_age
'
,
'
_gender
'
)
def
__init__
(self, name, age):
self._name
=
name
self._age
=
age
@property
def
name(self):
return
self._name
@property
def
age(self):
return
self._age
@age.setter
def
age(self, age):
self._age
=
age
def
play(self):
if
self._age <= 16
:
print
(
'
%s正在玩飛行棋 %s.
'
%
(self._name, self._gender))
else
:
print
(
'
%s正在玩斗地主 %s
'
%
(self._name, self._gender))
if
__name__
==
"
__main__
"
:
person
= Person(
'
王大錘
'
, 22
)
person._gender
=
'
男
'
person.play()
3? ?靜態(tài)方法和類方法
之前,我們在類中定義的方法都是對象方法,也就是說這些方法都是發(fā)送給對象的消息。實際上,我們寫在類中的方法并不需要都是對象方法,例如我們定義一個“三角形”類,通過傳入三條邊長來構造三角形,并提供計算周長和面積的方法,但是傳入的三條邊長未必能構造出三角形對象,因此我們可以先寫一個方法來驗證三條邊長是否可以構成三角形,這個方法很顯然就不是對象方法,因為在調(diào)用這個方法時三角形對象尚未創(chuàng)建出來(因為都不知道三條邊能不能構成三角形),所以這個方法是屬于三角形類而并不屬于三角形對象的。我們可以使用靜態(tài)方法來解決這類問題,代碼如下所示。
from
math
import
sqrt
class
Triangle(object):
def
__init__
(self, a, b, c):
self._a
=
a
self._b
=
b
self._c
=
c
@staticmethod
def
is_valid(a, b, c):
return
a + b > c
and
b + c > a
and
a + c >
b
def
perimeter(self):
return
self._a + self._b +
self._c
def
area(self):
half
= self.perimeter() / 2
return
sqrt(half * (half - self._a) *
(half
- self._b) * (half -
self._c))
def
main():
a, b, c
= 3, 4, 5
#
靜態(tài)方法和類方法都是通過給類發(fā)消息來調(diào)用的
if
Triangle.is_valid(a, b, c):
t
=
Triangle(a, b, c)
print
(t.perimeter())
#
也可以通過給類發(fā)消息來調(diào)用對象方法但是要傳入接收消息的對象作為參數(shù)
#
print(Triangle.perimeter(t))
print
(t.area())
#
print(Triangle.area(t))
else
:
print
(
'
無法構成三角形.
'
)
if
__name__
==
'
__main__
'
:
main()
4? ?繼承和多態(tài)
剛才我們提到了,可以在已有類的基礎上創(chuàng)建新類,這其中的一種做法就是讓一個類從另一個類那里將屬性和方法直接繼承下來,從而減少重復代碼的編寫。提供繼承信息的我們稱之為父類,也叫超類或基類;得到繼承信息的我們稱之為子類,也叫派生類或衍生類。子類除了繼承父類提供的屬性和方法,還可以定義自己特有的屬性和方法,所以子類比父類擁有的更多的能力,在實際開發(fā)中,我們經(jīng)常會用子類對象去替換掉一個父類對象,這是面向?qū)ο缶幊讨幸粋€常見的行為,對應的原則稱之為里氏替換原則。下面我們先看一個繼承的例子。
class
Person(object):
def
__init__
(self, name, age):
self._name
=
name
self._age
=
age
@property
def
name(self):
return
self._name
@property
def
age(self):
return
self._age
@age.setter
def
age(self, age):
self._age
=
age
def
study(self):
print
(
'
%s study python ing
'
%
self._name)
def
eating(self):
if
self._age >= 18
:
print
(
'
%s eat banana ing
'
%
self._name)
else
:
print
(
'
%s eat watermelon ing
'
%
self._name)
class
Student(Person):
def
__init__
(self, name, age, grade):
super().
__init__
(name, age)
self._grade
=
grade
@property
def
grade(self):
return
self._grade
@grade.setter
def
grade(self, grade):
self._grade
=
grade
def
study(self, course):
print
(
'
%s的%s正在學習%s.
'
%
(self._grade, self._name, course))
class
Teacher(Person):
def
__init__
(self, name, age, title):
super().
__init__
(name, age)
self._title
=
title
@property
def
title(self):
return
self._title
@title.setter
def
title(self, title):
self._title
=
title
def
teach(self, course):
print
(
'
%s%s正在講%s.
'
%
(self._name, self._title, course))
if
__name__
==
'
__main__
'
:
stu
= Student(
'
wangdachui
'
, 15,
'
初三
'
)
stu.study(
'
數(shù)學
'
)
stu.eating()
t
= Teacher(
'
lancer
'
, 38,
'
loser
'
)
t.teach(
'
Python程序設計
'
)
t.eating()
?
更多文章、技術交流、商務合作、聯(lián)系博主
微信掃碼或搜索:z360901061
微信掃一掃加我為好友
QQ號聯(lián)系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元

