看到這個(gè)題目,很多人會(huì)問(wèn)什么是面向?qū)ο缶幊?,也許也有人說(shuō)我只知道面向過(guò)程編程,那么我們來(lái)說(shuō)說(shuō)這兩種編程。通俗一點(diǎn)講面向過(guò)程就是你想干嘛,就直接寫個(gè)功能函數(shù)來(lái)實(shí)現(xiàn)你想做的事,它面向的是以動(dòng)作為主導(dǎo),通過(guò)函數(shù)或者方法來(lái)實(shí)現(xiàn);而面向?qū)ο?span style="color:#ff9900;">主導(dǎo)因素是對(duì)象,實(shí)現(xiàn)的不是函數(shù),而是“類”。把一組數(shù)據(jù)結(jié)構(gòu)和處理它們的方法組成對(duì)象(object),把相同行為的對(duì)象歸納為類(class)。類就是對(duì)對(duì)象的抽象,而對(duì)象是類的實(shí)例。那么下面我們主要針對(duì)類的定義和特征(封裝、繼承、多態(tài))進(jìn)行分析。
在python中使用'class’關(guān)鍵字定義類,并以冒號(hào)結(jié)束。然后在類中定義一些屬性和通過(guò)之前學(xué)習(xí)過(guò)的函數(shù)來(lái)定義方法,這樣我們就可以把對(duì)象的動(dòng)態(tài)特征描述出來(lái)。具體使用如下:
class 類名(object):
# __init__是一個(gè)特殊方法用于在創(chuàng)建對(duì)象時(shí)進(jìn)行初始化操作
def __init__(self, 參數(shù)):
初始化代碼
代碼如下所示:
- class Teacher(object):
- # __init__是一個(gè)特殊方法用于在創(chuàng)建對(duì)象時(shí)進(jìn)行初始化操作
- # 通過(guò)這個(gè)方法我們可以為老師對(duì)象綁定name和age兩個(gè)屬性
- def __init__(self, name, age):
- self.name = name
- self.age = age
- def teach(self, course_name):
- print('%s正在教%s.' % (self.name, course_name))
當(dāng)我們定義好一個(gè)類之后,可以通過(guò)下面的方式來(lái)創(chuàng)建對(duì)象并給對(duì)象發(fā)消息。代碼如下:
- def main():
- # 創(chuàng)建教師對(duì)象并指定姓名和年齡
- teacher1 = Teacher('諸葛亮', 38)
- # 給對(duì)象發(fā)teach消息
- teacher1.teach('Python程序設(shè)計(jì)')
- if __name__ == '__main__':
- main()
類的三種方法,分別是類方法、普通方法和靜態(tài)方法,如下是這三種方法的概念:
靜態(tài)方法: 用 @staticmethod 裝飾的不帶 self 參數(shù)的方法叫做靜態(tài)方法,類的靜態(tài)方法可以沒(méi)有參數(shù),可以直接使用類名調(diào)用。
普通方法: 默認(rèn)有個(gè)self參數(shù),且只能被對(duì)象調(diào)用。
類方法: 默認(rèn)有個(gè) cls 參數(shù),可以被類和對(duì)象調(diào)用,需要加上 @classmethod 裝飾器。
在這里主要聊聊靜態(tài)方法和類方法,現(xiàn)在我們先說(shuō)靜態(tài)方法,從定義中我們知道靜態(tài)方法不屬于任何一個(gè)對(duì)象的。例如,我們定義一個(gè)“三角形”類的時(shí)候,我們會(huì)定義很多相關(guān)的方法,如計(jì)算周長(zhǎng)和面積,但是我們還有一個(gè)判斷三條邊長(zhǎng)是否可以構(gòu)成三角形的方式,顯然它不屬于對(duì)象方法。為了解決這種問(wèn)題,我們使用靜態(tài)方法來(lá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)方法和類方法都是通過(guò)給類發(fā)消息來(lái)調(diào)用的 if Triangle.is_valid(a, b, c): t = Triangle(a, b, c) print(t.perimeter()) # 也可以通過(guò)給類發(fā)消息來(lái)調(diào)用對(duì)象方法但是要傳入接收消息的對(duì)象作為參數(shù) # print(Triangle.perimeter(t)) print(t.area()) # print(Triangle.area(t)) else: print('無(wú)法構(gòu)成三角形.') if __name__ == '__main__': main()
其實(shí)類中還有一種方法和靜態(tài)方法比較類似,它就是類方法。它代表的是當(dāng)前類相關(guān)的信息的對(duì)象(類本身也是一個(gè)對(duì)象,有的地方也稱之為類的元數(shù)據(jù)對(duì)象),通過(guò)這個(gè)參數(shù)我們可以獲取和類相關(guān)的信息并且可以創(chuàng)建出類的對(duì)象,代碼如下所示:
from time import time, localtime, sleep class Clock(object): """數(shù)字時(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): """顯示時(shí)間""" return '%02d:%02d:%02d' % (self._hour, self._minute, self._second) def main(): # 通過(guò)類方法創(chuàng)建對(duì)象并獲取系統(tǒng)時(shí)間 clock = Clock.now() while True: print(clock.show()) sleep(1) if __name__ == '__main__': main()
類和類之間的關(guān)系有三種:is-a、has-a和use-a關(guān)系
is-a關(guān)系也叫繼承或泛化,比如動(dòng)物和貓的關(guān)系屬于繼承關(guān)系
has-a關(guān)系通常稱之為關(guān)聯(lián),比如學(xué)校和教師的關(guān)系屬于關(guān)聯(lián)關(guān)系
use-a關(guān)系通常稱之為依賴,比如人和空氣的關(guān)系屬于依賴關(guān)系
這里我們主要舉例講解has-a和use-a關(guān)系,is-a關(guān)系在繼承和多態(tài)中舉例說(shuō)明,代碼如下:
#has-a關(guān)系 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關(guān)系 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('清華大學(xué)') t1 = Teacher('李老師') t2 = Teacher('劉老師') x.join_us(t1) x.join_us(t2) x.to_teach()
繼承是啥,繼承就是在原有的類基礎(chǔ)上創(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()
子類在繼承了父類的方法后,可以對(duì)父類已有的方法給出新的實(shí)現(xiàn)版本,這個(gè)動(dòng)作稱之為方法重寫(override)。通過(guò)方法重寫我們可以讓父類的同一個(gè)行為在子類中擁有不同的實(shí)現(xiàn)版本,當(dāng)我們調(diào)用這個(gè)經(jīng)過(guò)子類重寫的方法時(shí),不同的子類對(duì)象會(huì)表現(xiàn)出不同的行為,這個(gè)就是多態(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`類處理成了一個(gè)抽象類,所謂抽象類就是不能夠創(chuàng)建對(duì)象的類,這種類的存在就是專門為了讓其他類去繼承它。python通過(guò)`abc`模塊的`ABCMeta`元類和`abstractmethod`包裝器來(lái)達(dá)到抽象類的效果,如果一個(gè)類中存在抽象方法那么這個(gè)類就不能夠?qū)嵗▌?chuàng)建對(duì)象)。上面的代碼中,`Dog`和`Cat`兩個(gè)子類分別對(duì)`Animal`類中的`show`抽象方法進(jìn)行了重寫并給出了不同的實(shí)現(xiàn)版本,當(dāng)我們調(diào)用該方法時(shí),這個(gè)方法就表現(xiàn)出了多態(tài)行為。
在其他高級(jí)語(yǔ)言(c++,c#)中,對(duì)屬性和方式都有訪問(wèn)權(quán)限(也稱為可見(jiàn)性),主要有三種訪問(wèn)權(quán)限:私有的(private)或受保護(hù)的(protected)和 公開(kāi)的(public)。但python中的屬性和方法的訪問(wèn)權(quán)限只有兩種,也就是公開(kāi)的和私有的,如果希望是私有的,則在給其命名時(shí)可以用兩個(gè)下劃線作為開(kāi)頭。對(duì)屬性還可以通過(guò)使用@property包裝器來(lái)包裝getter和setter方法來(lái)對(duì)屬性的訪問(wèn)既安全又方便。代碼如下:
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動(dòng)態(tài)語(yǔ)言允許我們?cè)诔绦蜻\(yùn)行時(shí)給對(duì)象綁定新的屬性或方法,當(dāng)然也可以對(duì)已經(jīng)綁定的屬性和方法進(jìn)行解綁定。但是如果我們需要限定自定義類型的對(duì)象只能綁定某些屬性,可以通過(guò)在類中定義__slots__變量來(lái)進(jìn)行限定。需要注意的是__slots__的限定只對(duì)當(dāng)前類的對(duì)象生效,對(duì)子類并不起任何作用。例如:
class Person(object): # 限定Person對(duì)象只能綁定_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
聯(lián)系客服