第7章 面向對象
面向過程的程序設計把計算機程序視為一系列的命令集合,即一組函數的順序執行。為了簡化程序設計,面向過程把函數繼續切分為子函數,即把大塊函數通過切割成小塊函數來降低系統的復雜度。
而面向對象的程序設計把計算機程序視為一組對象的集合,而每個對象都可以接收其他對象發過來的消息,並處理這些消息,計算機程序的執行就是一系列消息在各個對象之間傳遞。
假設我們要處理學生的成績表,為了表示一個學生的成績,面向過程的程序可以用一個dict表示:
std1 = { ‘name‘: ‘Michael‘, ‘score‘: 98 }
std2 = { ‘name‘: ‘Bob‘, ‘score‘: 81 }
而處理學生成績可以通過函數實現,比如打印學生的成績:
def print_score(std):
print(‘%s: %s‘ % (std[‘name‘], std[‘score‘]))
如果采用面向對象的程序設計思想,我們首選思考的不是程序的執行流程,而是Student這種數據類型應該被視為一個對象,這個對象擁有name和score這兩個屬性(Property)。如果要打印一個學生的成績,首先必須創建出這個學生對應的對象,然後,給對象發一個print_score消息,讓對象自己把自己的數據打印出來。
class Student(object):
def __init__(self, name, score):
self.name = name
self.score = score
def print_score(self):
print(‘%s: %s‘ % (self.name, self.score))
給對象發消息實際上就是調用對象對應的關聯函數,我們稱之為對象的方法(Method)。面向對象的程序寫出來就像這樣:
bart = Student(‘Bart Simpson‘, 59)
lisa = Student(‘Lisa Simpson‘, 87)
bart.print_score()
lisa.print_score()
類與實例:
面向對象最重要的概念就是類(Class)和實例(Instance),必須牢記類是抽象的模板,比如Student類,而實例是根據類創建出來的一個個具體的“對象”,每個對象都擁有相同的方法,但各自的數據可能不同。
仍以Student類為例,在Python中,定義類是通過class關鍵字:
class Student(object):
pass
class後面緊接著是類名,即Student,類名通常是大寫開頭的單詞,緊接著是(object),表示該類是從哪個類繼承下來的,繼承的概念我們後面再講,通常,如果沒有合適的繼承類,就使用object類,這是所有類最終都會繼承的類。
定義好了Student類,就可以根據Student類創建出Student的實例,創建實例是通過類名+()實現的:
>>> bart = Student()
>>> bart
<__main__.Student object at 0x10a67a590>
>>> Student
<class ‘__main__.Student‘>
可以看到,變量bart指向的就是一個Student的實例,後面的0x10a67a590是內存地址,每個object的地址都不一樣,而Student本身則是一個類。
可以自由地給一個實例變量綁定屬性,比如,給實例bart綁定一個name屬性:
>>> bart.name = ‘Bart Simpson‘
>>> bart.name
‘Bart Simpson‘
由於類可以起到模板的作用,因此,可以在創建實例的時候,把一些我們認為必須綁定的屬性強制填寫進去。通過定義一個特殊的__init__方法,在創建實例的時候,就把name,score等屬性綁上去:
class Student(object):
def __init__(self, name, score):
self.name = name
self.score = score
註意到__init__方法的第一個參數永遠是self,表示創建的實例本身,因此,在__init__方法內部,就可以把各種屬性綁定到self,因為self就指向創建的實例本身。
有了__init__方法,在創建實例的時候,就不能傳入空的參數了,必須傳入與__init__方法匹配的參數,但self不需要傳,Python解釋器自己會把實例變量傳進去:
>>> bart = Student(‘Bart Simpson‘, 59)
>>> bart.name
‘Bart Simpson‘
>>> bart.score
59
和普通的函數相比,在類中定義的函數只有一點不同,就是第一個參數永遠是實例變量self,並且,調用時,不用傳遞該參數。除此之外,類的方法和普通函數沒有什麽區別,所以,你仍然可以用默認參數、可變參數、關鍵字參數和命名關鍵字參數
類的屬性:
# class OldboyStudent:
# school = ‘oldboy‘ #類的數據屬性
# def learn(self): #類的函數屬性
# print(‘is learning‘)
#
# def eat(self):
# print(‘is eating‘)
# print(‘======>‘)
#類體的代碼在類定義階段就會執行,理所應當會產生類的名稱空間,用__dict__屬性查看
# print(OldboyStudent.__dict__)
# print(OldboyStudent.__dict__[‘school‘])
# print(OldboyStudent.__dict__[‘learn‘])
#類的屬性操作
# print(OldboyStudent.school)
# print(OldboyStudent.learn)
#
# def func():pass
# print(func)
# OldboyStudent.learn(123)
# OldboyStudent.x=1111111111111111111111
# OldboyStudent.school=‘Oldboy‘
# del OldboyStudent.school
# print(OldboyStudent.__dict__)
# OldboyStudent.__dict__[‘x‘]=1111111111111111111111
#產生程序中的對象:類名加括號,調用類,產生一個該類的實際存在的對象,該調用過程稱為實例化,產生的結果又可以成為實例
# class OldboyStudent:
# school = ‘oldboy‘
# #obj1,‘李大炮‘,18,‘女‘
# def __init__(self,name,age,sex): #在實例化時,產生對象之後執行
# # if not isinstance(name,str):
# # raise TypeError(‘名字必須是字符串類型‘)
# self.name=name
# self.age=age
# self.sex=sex
# # return None #__init__方法必須返回None
# #obj1.name=‘李大炮‘
# #obj1.age=18
# #obj1.sex=‘女‘
#
# def learn(self):
# print(‘is learning‘)
#
# def eat(self):
# print(‘is eating‘)
# obj1=OldboyStudent(‘李大炮‘,18,‘女‘) #
# 分兩步:
# 第一步:先產生一個空對象obj1
# 第二步:OldboyStudent.__init__(obj1,‘李大炮‘,18,‘女‘)
# print(obj1.__dict__)
#
# obj2=OldboyStudent(‘張全蛋‘,28,‘男‘)
# obj3=OldboyStudent(‘牛榴彈‘,18,‘女‘)
#
# print(obj2.__dict__)
# print(obj3.__dict__)
#
#
#
# print(obj1.name)#obj1.__dict__[‘name‘]
#
# obj1.name=‘大炮‘
# print(obj1.__dict__)
# obj1.__dict__[‘name‘]=‘炮‘
# print(obj1.name)
#
# obj1.__dict__.pop(‘name‘)
# # print(obj1.name)
# print(obj1.__dict__)
#繼承的基本形式
當我們定義一個class的時候,可以從某個現有的class繼承,新的class稱為子類(Subclass),而被繼承的class稱為基類、父類或超類(Base class、Super class)。
定義
# class ParentClass1(object): #定義父類
# pass
#
# class ParentClass2: #定義父類
# pass
#
# class SubClass1(ParentClass1): #單繼承,基類是ParentClass1,派生類是SubClass
# pass
#
# class SubClass2(ParentClass1,ParentClass2): #python支持多繼承,用逗號分隔開多個繼承的類
# pass
#
#
#
#
# print(SubClass1.__bases__)
# print(SubClass2.__bases__)
# print(ParentClass1.__bases__)
比如,我們已經編寫了一個名為Animal的class,有一個run()方法可以直接打印:
class Animal(object):
def run(self):
print(‘Animal is running...‘)
當我們需要編寫Dog和Cat類時,就可以直接從Animal類繼承:
class Dog(Animal):
pass
class Cat(Animal):
pass
對於Dog來說,Animal就是它的父類,對於Animal來說,Dog就是它的子類。Cat和Dog類似。
繼承有什麽好處?最大的好處是子類獲得了父類的全部功能。由於Animial實現了run()方法,因此,Dog和Cat作為它的子類,什麽事也沒幹,就自動擁有了run()方法:
dog = Dog()
dog.run()
cat = Cat()
cat.run()
運行結果如下:
Animal is running...
Animal is running..
子類方法重用父類的功能
# class OldboyPeople:
# school = ‘oldboy‘
# def __init__(self,name,age,sex):
# self.name=name
# self.age=age
# self.sex=sex
#
# def eat(self):
# print(‘is eating‘)
# def teach(self):
# print(‘這是父類的teach‘)
#
# class OldboyTeacher(OldboyPeople):
# def __init__(self,name,age,sex,salary,title):
# # OldboyPeople.__init__(self,name,age,sex)
# #在Python2中需要寫全:super(OldboyTeacher,self)
# super().__init__(name,age,sex)
# self.salary=salary
# self.title=title
#
# def teach(self):
# # OldboyPeople.teach(self)
# super().teach()
# print(‘%s is teaching‘ %self.name)
# print(OldboyTeacher.mro())
#
# egon_obj=OldboyTeacher(‘egon‘,18,‘male‘,3.1,‘沙河霸道金牌講師‘)
# # print(egon_obj.title)
# # print(egon_obj.__dict__)
# egon_obj.teach()
總結:
1 繼承的功能之一:解決類與類之間的代碼重復問題
2 繼承是類與類之間的關系,是一種,什麽是什麽的關系
3 在子類派生出的新的屬性,已自己的為準
4 在子類派生出的新的方法內重用父類的功能的方式:指名道姓法OldboyPeople.__init__
這種調用方式本身與繼承是沒有關系
第7章 面向對象