看到這個題目,很多人會問什么是面向對象編程,也許也有人說我只知道面向過程編程,那么我們來說說這兩種編程。通俗一點講面向過程就是你想干嘛,就直接寫個功能函數(shù)來實現(xiàn)你想做的事,它面向的是以動作為主導,通過函數(shù)或者方法來實現(xiàn);而面向對象主導因素是對象,實現(xiàn)的不是函數(shù),而是“類”。把一組數(shù)據(jù)結構和處理它們的方法組成對象(object),把相同行為的對象歸納為類(class)。類就是對對象的抽象,而對象是類的實例。那么下面我們主要針對類的定義和特征(封裝、繼承、多態(tài))進行分析。
在python中使用'class’關鍵字定義類,并以冒號結束。然后在類中定義一些屬性和通過之前學習過的函數(shù)來定義方法,這樣我們就可以把對象的動態(tài)特征描述出來。具體使用如下:
class 類名(object):
# __init__是一個特殊方法用于在創(chuàng)建對象時進行初始化操作
def __init__(self, 參數(shù)):
初始化代碼
代碼如下所示:
- class Teacher(object):
- # __init__是一個特殊方法用于在創(chuàng)建對象時進行初始化操作
- # 通過這個方法我們可以為老師對象綁定name和age兩個屬性
- def __init__(self, name, age):
- self.name = name
- self.age = age
- def teach(self, course_name):
- print('%s正在教%s.' % (self.name, course_name))
當我們定義好一個類之后,可以通過下面的方式來創(chuàng)建對象并給對象發(fā)消息。代碼如下:
- def main():
- # 創(chuàng)建教師對象并指定姓名和年齡
- teacher1 = Teacher('諸葛亮', 38)
- # 給對象發(fā)teach消息
- teacher1.teach('Python程序設計')
- if __name__ == '__main__':
- main()
類的三種方法,分別是類方法、普通方法和靜態(tài)方法,如下是這三種方法的概念:
靜態(tài)方法: 用 @staticmethod 裝飾的不帶 self 參數(shù)的方法叫做靜態(tài)方法,類的靜態(tài)方法可以沒有參數(shù),可以直接使用類名調用。
普通方法: 默認有個self參數(shù),且只能被對象調用。
類方法: 默認有個 cls 參數(shù),可以被類和對象調用,需要加上 @classmethod 裝飾器。
在這里主要聊聊靜態(tài)方法和類方法,現(xiàn)在我們先說靜態(tài)方法,從定義中我們知道靜態(tài)方法不屬于任何一個對象的。例如,我們定義一個“三角形”類的時候,我們會定義很多相關的方法,如計算周長和面積,但是我們還有一個判斷三條邊長是否可以構成三角形的方式,顯然它不屬于對象方法。為了解決這種問題,我們使用靜態(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ā)消息來調用的 if Triangle.is_valid(a, b, c): t = Triangle(a, b, c) print(t.perimeter()) # 也可以通過給類發(fā)消息來調用對象方法但是要傳入接收消息的對象作為參數(shù) # print(Triangle.perimeter(t)) print(t.area()) # print(Triangle.area(t)) else: print('無法構成三角形.') if __name__ == '__main__': main()
其實類中還有一種方法和靜態(tài)方法比較類似,它就是類方法。它代表的是當前類相關的信息的對象(類本身也是一個對象,有的地方也稱之為類的元數(shù)據(jù)對象),通過這個參數(shù)我們可以獲取和類相關的信息并且可以創(chuàng)建出類的對象,代碼如下所示:
from time import time, localtime, sleep class Clock(object): """數(shù)字時鐘""" def __init__(self, hour=0, minute=0, second=0): self._hour = hour self._minute = minute self._second = second @classmethod def now(cls): ctime = localtime(time()) return cls(ctime.tm_hour, ctime.tm_min, ctime.tm_sec) def show(self): """顯示時間""" return '%02d:%02d:%02d' % (self._hour, self._minute, self._second) def main(): # 通過類方法創(chuàng)建對象并獲取系統(tǒng)時間 clock = Clock.now() while True: print(clock.show()) sleep(1) if __name__ == '__main__': main()
類和類之間的關系有三種:is-a、has-a和use-a關系
is-a關系也叫繼承或泛化,比如動物和貓的關系屬于繼承關系
has-a關系通常稱之為關聯(lián),比如學校和教師的關系屬于關聯(lián)關系
use-a關系通常稱之為依賴,比如人和空氣的關系屬于依賴關系
這里我們主要舉例講解has-a和use-a關系,is-a關系在繼承和多態(tài)中舉例說明,代碼如下:
#has-a關系 class Person: def play(self, tools): tools.run() print('終于能打游戲了') class Phone: def run(self): print('王者榮耀已經(jīng)登陸') PH = Phone() p = Person() p.play(PH) #use-a關系 class School: def __init__(self, name): self.teach_list = [] def join_us(self,teach): self.teach_list.append(teach) def to_teach(self): for t in self.teach_list: t.work() class Teacher: def __init__(self, name): self.name = name def work(self): print(f'{self.name}在上課') x = School('清華大學') t1 = Teacher('李老師') t2 = Teacher('劉老師') x.join_us(t1) x.join_us(t2) x.to_teach()
繼承是啥,繼承就是在原有的類基礎上創(chuàng)建新類,讓新類擁有原有類的屬性和方法,這就是繼承。提供繼承信息的叫做父類,也叫超類或基類;得到繼承信息的叫做子類,也叫派生類或衍生類。子類除了繼承父類提供的屬性和方法,還可以定義自己特有的屬性和方法,所以子類比父類擁有的更多的能力。繼承的例子如下:
class Animal(object): def __init__(self): self._name = "" self._age = 0 @property def age(self): return self._age @age.setter def age(self, age): self._age = age @property def name(self): return self._name @name.setter def name(self, name): self._name = name def show(self): print("%s, %d" % (self._name, self._age)) class Dog(Animal): def __init__(self): Animal.__init__(self) self._age = 1 self._name = "狗" class Cat(Animal): def __init__(self): super().__init__() self._name = "貓" self._age = 2 dog = Dog() dog.show() cat = Cat() cat.show()
子類在繼承了父類的方法后,可以對父類已有的方法給出新的實現(xiàn)版本,這個動作稱之為方法重寫(override)。通過方法重寫我們可以讓父類的同一個行為在子類中擁有不同的實現(xiàn)版本,當我們調用這個經(jīng)過子類重寫的方法時,不同的子類對象會表現(xiàn)出不同的行為,這個就是多態(tài)。多態(tài)代碼如下:
from abc import ABCMeta, abstractmethod class Animal(object, metaclass=ABCMeta): def __init__(self): self._name = "" self._age = 0 @abstractmethod def show(self): pass class Dog(Animal): def __init__(self): Animal.__init__(self) self._age = 1 self._name = "狗" def show(self): print("%s, %d 旺旺" % (self._name, self._age)) class Cat(Animal): def __init__(self): super().__init__() self._name = "貓" self._age = 2 def show(self): print("%s, %d 喵喵" % (self._name, self._age)) dog = Dog() dog.show() cat = Cat() cat.show()
在上面的代碼中,我們將`Animal`類處理成了一個抽象類,所謂抽象類就是不能夠創(chuàng)建對象的類,這種類的存在就是專門為了讓其他類去繼承它。python通過`abc`模塊的`ABCMeta`元類和`abstractmethod`包裝器來達到抽象類的效果,如果一個類中存在抽象方法那么這個類就不能夠實例化(創(chuàng)建對象)。上面的代碼中,`Dog`和`Cat`兩個子類分別對`Animal`類中的`show`抽象方法進行了重寫并給出了不同的實現(xiàn)版本,當我們調用該方法時,這個方法就表現(xiàn)出了多態(tài)行為。
在其他高級語言(c++,c#)中,對屬性和方式都有訪問權限(也稱為可見性),主要有三種訪問權限:私有的(private)或受保護的(protected)和 公開的(public)。但python中的屬性和方法的訪問權限只有兩種,也就是公開的和私有的,如果希望是私有的,則在給其命名時可以用兩個下劃線作為開頭。對屬性還可以通過使用@property包裝器來包裝getter和setter方法來對屬性的訪問既安全又方便。代碼如下:
class Pet(object): def __init__(self, name, age): self._name = name self._age = age @property def age(self): return self._age @age.setter def age(self, age): self._age = age @property def name(self): return self._name @name.setter def name(self, name): self._name = name def __make_voice(self): print("%s, %d 哈哈哈哈" % (self._name, self._age)) def show(self): print("%s, %d" % (self._name, self._age)) pet1= Pet("狗", 1) pet1.show() pet1.name = "貓" pet1.age= 5 pet1.show() # AttributeError: 'Pet' object has no attribute '__make_voice' #pet1.__make_voice()
Python動態(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正在玩飛行棋.' % self._name) else: print('%s正在玩斗地主.' % self._name) def main(): person = Person('諸葛亮', 22) person.play() person._gender = '男' # AttributeError: 'Person' object has no attribute '_is_gay' # person._is_gay = True