python學習筆記-4:面向對象


python學習筆記-4:面向對象


定義類

python類的概念與C++相同,都是面向對象思想的產物。具體的python中定義類的語法如下:

<code>class Student(object):

# __init__是一個特殊方法用於在創建對象時進行初始化操作
# 通過這個方法我們可以為學生對象綁定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)/<code>

創建對象和調用對象函數

<code>stu1 = Student('阿偉', 27)
# 給對象發study消息
stu1.study('Python程序設計')

# 給對象發watch_av消息
stu1.watch_movie()
stu2 = Student('王大錘', 15)
stu2.study('思想品德')
stu2.watch_movie()/<code>

靜態方法和類方法

python中的類,除了屬於對象本身的方法(也就是成員函數之外),還有專門的靜態方法和類方法。

靜態方法是屬於類本身的,而成員函數是由類定義,並屬於對象的,所以,靜態方法在定義對象之前就能使用。可以這麼理解,靜態方法是一個函數,一個獨立的函數,只不過這個函數定義寫在了類定義裡頭,使用這個函數直接ClassName.StaticMethod()即可,代碼已給出例子。

類方法,是屬於類本身的方法,同時python可以定義類本身的屬性。如代碼例子中的學生類的定義,定義了具體學生對象的名字與年齡屬性,而在類方法之前定義的count和name則是學生這個類本身的屬性,這裡定義了學生班級名字和學生數量作為學生這個類的屬性。既可以用靜態方法調用的方式調用類方法,也可以用調用對象成員函數的方法去調用類方法,不管是哪一種,最終作用的都是類的屬性,而不是對象的屬性。

<code>class Student(object): 

def __init__(self, name):
self.name = name
self.cc()

#類方法
count = 0
@classmethod
def cc(cls):
cls.count += 1
cls.name = '3年2班'
@classmethod
def cc1(cls, name):
cls.name = name

#靜態方法
@staticmethod
def check(a, b, c):
return a + b > c and b + c > a and a + c > b

a1 = Student('ee')
a2 = Student('rr')
print(a1.count)
print(a2.count)
a3 = Student('tt')
a4 = Student('yyu')
print(a1.count)
print(Student.count)
print(a1.name)
print(Student.name)
Student.cc1('3年3班')
print(Student.name)
print(a1.name)
a1.cc1('3年4班')
print(Student.name)
print(a1.name)

if Student.check(3, 4, 5):
print('可以組成三角形')/<code>

類的導入

把類名稱當成函數,與函數導入一樣導入即可。適用於函數導入的方法同樣適用於類的導入。

對象屬性訪問權限:@property裝飾器

C++中的對象屬性,一般要求作為private,即私有成員。外界無法直接獲取和修改對象的私有成員,只能通過成員函數進行獲取和修改。python中的對象成員則是通過@property裝飾器在類定義時將屬性設置為只讀或可讀寫,並同時生成類似於C++中獲取和修改屬性的成員函數。建議所有屬性都使用@property裝飾器,同時屬性名前綴下劃線_,以表明這是一個被隱藏的內部屬性。

在為一個類實例綁定屬性時,如果我們直接把屬性暴露出去,雖然寫起來很簡單,但是,沒辦法檢查參數,導致可以把成績隨便改,甚至類型錯誤都可以。

<code>class Student(object):

def __init__(self, score):
self.score = score


if __name__ == '__main__':
s = Student(100)
print(s.score)
s.score = 50
print(s.score)
s.score = 'abc'
print(s.score)/<code>

可以看到,直接訪問和修改對象屬性,由於python動態語言的特性,甚至可能使用錯誤的變量類型進行修改。使用裝飾器@property:

<code>class Student(object):

@property

def score(self):
return self._score

@score.setter
def score(self, value):
if not isinstance(value, int):
raise ValueError('score must be an integer !')
if value < 0 or value > 100:
raise ValueError('score must between 0 - 100 !')
self._score = value


if __name__ == '__main__':
s = Student()
s.score = 50 # 實際轉化為s.set_score()
print(s.score) # 實際轉化為s.get_score()
s.score = 101/<code>

@property本身又創建了另一個裝飾器@score.setter,負責對屬性進行賦值。本質上就是在把屬性設置為類似C++private成員的同時定義了讀取與賦值函數。只不過python把這兩步同時自動完成了。同時,定義了@property裝飾器而不定義@score.property裝飾器,則score將無法修改,只能讀取,也就是隻讀形式。

@property廣泛應用在類的定義中,可以讓調用者寫出簡短的代碼,同時保證對參數進行必要的檢查,這樣,程序運行時就減少了出錯的可能性。

繼承與多態

已有類的基礎上創建新類,這其中的一種做法就是讓一個類從另一個類那裡將屬性和方法直接繼承下來,從而減少重複代碼的編寫。提供繼承信息的我們稱之為父類,也叫超類或基類;得到繼承信息的我們稱之為子類,也叫派生類或衍生類。子類除了繼承父類提供的屬性和方法,還可以定義自己特有的屬性和方法,所以子類比父類擁有的更多的能力,在實際開發中,我們經常會用子類對象去替換掉一個父類對象,這是面向對象編程中一個常見的行為,對應的原則稱之為里氏替換原則。

繼承的例子:

<code>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 play(self):
print('%s正在愉快的玩耍.' % self._name)

def watch_av(self):
if self._age >= 18:
print('%s正在觀看愛情動作片.' % self._name)
else:
print('%s只能觀看《熊出沒》.' % 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))


def main():
stu = Student('王大錘', 15, '初三')
stu.study('數學')
stu.watch_av()
t = Teacher('阿偉', 25, '磚家')
t.teach('Python程序設計')
t.watch_av()


if __name__ == '__main__':
main()/<code>

子類在繼承了父類的方法後,可以對父類已有的方法給出新的實現版本,這個動作稱之為方法重寫/重載(override)。通過方法重寫我們可以讓父類的同一個行為在子類中擁有不同的實現版本,當我們調用這個經過子類重寫的方法時,不同的子類對象會表現出不同的行為,這個就是多態(poly-morphism)。

<code>from abc import ABCMeta, abstractmethod


class Pet(object, metaclass=ABCMeta):
"""寵物"""

def __init__(self, nickname):
self._nickname = nickname

@abstractmethod
def make_voice(self):
"""發出聲音"""
pass


class Dog(Pet):
"""狗"""

def make_voice(self):
print('%s: 汪汪汪...' % self._nickname)


class Cat(Pet):
"""貓"""

def make_voice(self):
print('%s: 喵...喵...' % self._nickname)


def main():
pets = [Dog('旺財'), Cat('凱蒂'), Dog('大黃')]
for pet in pets:
pet.make_voice()


if __name__ == '__main__':
main()/<code>

在上面的代碼中,我們將Pet類處理成了一個抽象類,所謂抽象類就是不能夠創建對象的類,這種類的存在就是專門為了讓其他類去繼承它。Python從語法層面並沒有像Java或C#那樣提供對抽象類的支持,但是我們可以通過abc模塊的ABCMeta元類和abstractmethod包裝器來達到抽象類的效果,如果一個類中存在抽象方法那麼這個類就不能夠實例化(創建對象)。上面的代碼中,Dog和Cat兩個子類分別對Pet類中的make_voice抽象方法進行了重寫並給出了不同的實現版本,當我們在main函數中調用該方法時,這個方法就表現出了多態行為(同樣的方法做了不同的事情)。

當然,非抽象類的函數重載方式是一樣的:

<code>class Pet(object):
"""寵物"""

def __init__(self, nickname):
self._nickname = nickname

def make_voice(self):
"""發出聲音"""
pass


class Dog(Pet):
"""狗"""

def make_voice(self):
print('%s: 汪汪汪...' % self._nickname)


class Cat(Pet):
"""貓"""

def make_voice(self):
print('%s: 喵...喵...' % self._nickname)


def main():
pets = [Dog('旺財'), Cat('凱蒂'), Dog('大黃')]
for pet in pets:
pet.make_voice()


if __name__ == '__main__':
main()/<code>


分享到:


相關文章: