Python學習之==>面向對象編程
一、面向對象與面向過程
面向對象與面向過程是兩種不同的編程範式,範式指的是按照什麽方式去編程、去實現一個功能。不同的編程範式本質上代表對各種不同類型的任務采取不同的解決問題的思路。
1、面向過程編程
角色是執行者,把一個項目按照一定的順序,從頭到尾一步步執行下去。這種思想好理解,但只要前面一個步驟變了,後面的步驟也要跟著變,維護起來比較麻煩。
2、面向對象編程
角色是指揮者,把一個項目分成一個個小的部分,每個部分負責一方面的功能,整個項目由這些部分組合而成一個整體。類似一個機關,分為各個職能部門,只要符合一定的前提就行。面向對象的思想適合多人分工合作。
面向對象是包含面向過程思路的,比如定義類中的方法,每個小方法、小功能還是面向過程的的思想。
面向對象與面向過程的主要區別就是:面向對象可以使程序更加容易更改和擴展。
二、面向對象的特性
1、類
Class,相當於一個種類、一個模型。一個類就是對一類擁有相同屬性的對象的抽象、藍圖、原形。在類中定義了這些對象都具備的屬性、共同的方法。
2、對象(實例)
Object,根據模型造出來的具體的東西。一個對象就是一個類實例化後實例。一個類必須經過實例化後才能在程序中調用,一個類可以實例化多個對象,每個對象也可以有不同的屬性。(對象就是實例,兩者是一個意思)
3、實例化
初始化一個類,創造一個對象。把一個類變成一個具體的對象的過程,叫做實例化。
4、屬性
屬性就是類裏面的一個變量,有類屬性(類變量)和實例屬性(實例變量)兩種。類屬性是在定義的時候就有的,實例屬性是在實例化的時候才產生的變量。舉個例子來說明類屬性於實例屬性:
類屬性(類變量):公共的變量,每個實例都可以用。直接通過“類名.XXX”來進行調用和修改,不需要實例化
實例屬性(實例變量):必須先進行實例化,然後通過“實例名.XXX”來進行調用和修改。
下面簡單定義一個類來說明以上概念,類的定義使用class關鍵字,類名的首字母大寫。
1 class Person(): # 經典類,新式類為:class Person(object): 2 hand = 2 # 類屬性 3 def __init__(self,name): # 構造函數,類在實例化的時候自動執行的函數 4 # self代表的是實例化之後的對象5 self.name = name # 實例屬性 6 self.nose = 1 # 實例屬性 7 print(‘開始創造機器人。。。‘) 8 def driver(self): # 實例方法 9 print(‘%s開車中。。‘%self.name) 10 self.eat() # 調用類裏面的方法 11 def fly(self): # 實例方法 12 print(‘%s,飛吧。。‘%self.name) # 獲取類裏面的變量 13 def eat(self): # 實例方法 14 print(‘%s吃吧,管飽‘%self.name) 15 16 # 類在用的時候,首先需要實例化 17 zlj = Person(‘張流量‘) # 實例化:類名+括號 18 print(zlj.name) # 調用實例屬性 19 print(zlj.nose) # 調用實例屬性 20 print(zlj.hand) # 調用類屬性 21 zlj.hand = 5 # 不會改變類變量,只影響實例裏面的變量 22 print(zlj.hand) # 5 23 zlj.sex = ‘男‘ # 為實例增加實例屬性 24 print(zlj.sex) 25 zlj.driver() # 調用類方法 26 zlj.fly() # 調用類方法 27 dcg = Person(‘董春光‘) # 實例化dcg 28 print(dcg.hand) # 2 29 Person.hand = 10 # 修改類變量 30 print(dcg.hand) # 10 31 print(zlj.hand) # 5,修改類變量發生在修改實例變量之後,所以實例變量的值不再發生改變
5、方法
方法就是類的功能,是定義在類裏面的函數
(1)類方法(cls):@classmethod
a、不用實例化就可以直接調用
b、它可以通過cls使用類變量
c、它不能調用實例變量和實例方法
d、不想實例化的時候,可以把方法定義成類方法
(2)實例方法:self
實例化後才能使用的方法
(3)屬性方法(self):@property
看起來很像屬性的一個方法,將沒有入參的函數變為一個變為一個屬性方法(類似於變量),結果是函數的返回值
(4)靜態方法():@staticmethod
靜態方法就是一個普通的函數,只不過寫在類裏面而已。它用不了類屬性(變量)、類方法、實例屬性(變量)、實例方法
1 class Baby(object): 2 nationality = ‘China‘ # 類變量,公共變量,每個實例都可以用 3 def __init__(self): # 構造函數,類在實例化的時候自動執行的函數 4 self.name = ‘牛牛‘ # 實例變量,必須實例化才能調用 5 def my(self): # 實例方法,必須實例化才能調用 6 self.sex = ‘男‘ # 實例變量,必須實例化才能調用 7 def cry(self): # 實例方法,必須實例化才能調用 8 print(‘嗚嗚嗚‘) 9 @property # 屬性方法,直接返回函數執行結果 10 def AAA(self): 11 return 98 12 @classmethod # 類方法,不用實例化就可以直接調用 13 def xm(cls): # cls代表的就是Baby類 14 print(cls.nationality) 15 # cls.name # 報錯,不能調用類中其他的實例變量 16 # cls.my() # 報錯,不能調用勒種其他的實例方法 17 print(‘我是類方法‘) 18 @staticmethod # 靜態方法 19 def xh(): 20 print(‘這個是靜態方法,它和沒寫在類裏面的函數一樣‘) 21 22 # 類方法:不想實例化的時候可以定義成類方法 23 # 1、不用實例化就可以直接調用 24 # 2、它可以通過cls使用類變量 25 # 3、它不能調用這個類裏面的其他實例方法和實例變量 26 Baby.xm() # 不用實例化直接用類名調用類方法 27 28 # 實例方法 29 Zll = Baby() 30 Zll.my() 31 print(Zll.name) 32 Zll.xm() # 實例化後通過對象調用類方法 33 34 # 屬性方法 35 Dcg = Baby() 36 print(Dcg.AAA) # 調用屬性方法不用加括號 37 38 # 靜態方法: 39 # 1、就是一個普通函數,只不過是寫在類裏面而已 40 # 2、它用不了類變量、類方法、實例變量、實例方法 41 Hr = Baby() 42 Hr.xh()
6、繼承
一個類可以派生出子類,在父類裏面定義的屬性和方法自動被子類繼承,繼承是為了代碼重用。並且,子類可以重寫父類的方法。Python3中經典類和新式類的多繼承都是廣度優先。但在Python2中,經典類和新式類的多繼承是有區別的:經典類的多繼承是深度優先,新式類的多繼承是廣度優先。
1 class Father(object): # 父類 2 def __init__(self): 3 self.money = 1000000 4 self.house = ‘5環20套‘ 5 def sing(self): 6 print(‘唱歌‘) 7 def dance(self): 8 print(‘跳廣場舞‘) 9 def mf(self): 10 print(‘找朋友‘) 11 class Son(Father): # 繼承類:將父類名寫在括號裏 12 def dance(self): 13 self.house = ‘2環1套‘ 14 print(‘跳霹靂舞‘) 15 16 # 通過繼承類調用父類中的方法和變量 17 m = Son() # 實例化 18 print(m.money) # 調用父類中的實例變量 19 m.sing() # 調用父類中的實例方法 20 m.dance() # 子類和父類中都存在的方法則用子類中的方法 21 print(m.house) # 子類和父類中都存在的變量則用子類中的變量
重寫父類的方法:
(1)父類方法沒用,需要重寫
(2)父類方法不夠完善,想給這個方法在原有的基礎上添加一些功能
1 class Zll(): 2 pass 3 4 class Dcg(): 5 def smile(self): 6 print(‘喔喔喔‘) 7 8 class Lw(): 9 def smile(self): 10 print(‘啊啊啊‘) 11 12 class Xz(Zll,Dcg,Lw): # 子類可以繼承多個父類 13 def smile(self): # 重寫父類的方法 14 Lw().smile() # 調用父類,指定哪個父類 15 super(Xz,self).smile() # 自動找到父類,先找到哪個類用哪個類 16 print(‘呵呵呵‘) 17 18 A = Xz() 19 A.smile()
經典類與新式類的區別:
在Python3中經典類和新式類沒有區別,在Python2中區別如下:
(1)多繼承的時候:
經典類:深度優先
新式類:廣度優先
Python3裏面都是廣度優先
(2)Python2裏面經典類不能用super,新式類可以用
1 # class A(): # 經典類 2 class A(object): # 新式類 3 def x(self): 4 print(‘A‘) 5 class B(A): 6 pass 7 class C(A): 8 def x(self): 9 print(‘C‘) 10 class D(B,C): 11 pass 12 13 S = D() 14 S.x() 15 # 運行結果: 16 # Python2經典類:A(先從B裏面找,B裏面沒有再去A裏面找,A裏面沒有再去C裏面找,B→A→C) 17 # Python2新式類:C(先從B裏面找,B裏面沒有再去C裏面找,C裏面沒有再去A裏面找,B→C→A) 18 # Python3:C(和Python2中新式類一樣)
7、封裝
把一些功能的實現細節隱藏,但類中對數據的賦值、內部調用對外部用戶卻是透明的,使類變成一個膠囊或容器,裏面包含著類的數據和方法(比如說創造一個人,把身體內部的心肝脾肺腎都封裝起來了,其他人只能直接找這個人而看不到裏面有上面東西)。
8、多態
對不同類的對象發出相同的消息將會有不同的行為。比如,你的老板讓所有員工在九點鐘開始工作, 他只要在九點鐘的時候說:“開始工作”即可,而不需要對銷售人員說:“開始銷售工作”,對技術人員說:“開始技術工作”, 因為“員工”是一個抽象的事物, 只要是員工就可以開始工作,他知道這一點就行了。至於每個員工,當然會各司其職,做各自的工作。
多態就是抽象化的一種體現,把一系列具體事物的共同點抽象出來, 再通過這個抽象的事物, 與不同的具體事物進行對話。一種接口,多種實現。
1 class Animal(object): # 父類 2 def __init__(self, name): 3 self.name = name 4 class Dog(Animal): # 狗類,有叫的方法 5 def cry(self): 6 print(‘狗 [%s] 汪汪汪‘ % self.name) 7 class Cat(Animal): # 貓類,有叫的方法 8 def cry(self): 9 print(‘貓 [%s] 喵喵喵‘ % self.name) 10 def cry(obj): # 定義一個函數,去調用傳進來的實例的cry方法 11 obj.cry() 12 13 d1 = Dog(‘大黃‘) # 實例化狗 14 d2 = Dog(‘小黃‘) # 實例化狗 15 c1 = Cat(‘小白‘) # 實例化貓 16 c2 = Cat(‘小黑‘) # 實例化貓 17 cry(d1) # 把對象d1傳進來 18 cry(d2) # 把對象d2傳進來 19 objs = [d1, d2, c1, c2] # 把上面實例化的對象都放到一個list裏面 20 for obj in objs: # 循環統一調用 21 cry(obj)
三、本類對象
類中的self代表的是本類對象,也就是實例化後的對象。因為函數裏面的變量都是局部變量,出了函數就不能使用,用self綁定之後,在類中任何地方都能隨便使用(self.XXX)。
1 class Baby: 2 def __init__(self,name): 3 #name = name # 局部變量,出了函數就失效 4 self.name = name # 在類中self.name可以隨便用了 5 print(‘self的內存地址‘, id(self)) 6 def cry(self): 7 print(‘%s在哭‘%self.name) # self.name在類中其他方法可以使用 8 Zll = Baby(‘張流量‘) # 實例化時將Amy的地址給self 9 print(‘實例的內存地址‘,id(Zll)) # 與實例化時self的內存地址一致 10 Zll.cry() 11 # 通過運行發現self的內存地址和實例的內存地址是一樣的,說明self代表的就是實例化後的對象Zll
四、構造函數與析構函數
1、構造函數
類在實例化時自動執行的函數,但類中不是必須包含構造函數的。
實例化時只有構造函數會自動執行,其他函數不會被執行。
1 class Baby(object): 2 def __init__(self): # 構造函數,類在實例化的時候自動執行的函數 3 self.name = ‘牛牛‘ # 實例變量,必須實例化才能調用 4 def my(self): # 實例方法,必須實例化才能調用 5 self.sex = ‘男‘ # 實例變量,必須實例化才能調用 6 7 Zll = Baby() 8 print(Zll.name) # 實例化時構造函數自動執行 9 print(Zll.sex) # my方法未被執行,所以會報錯--‘Baby‘ object has no attribute ‘sex‘
解決Zll.sex報錯有兩種方式:
(1)在構造函數中調用一次my函數,如下:
1 class Baby(object): 2 def __init__(self): # 構造函數,類在實例化的時候自動執行的函數 3 self.name = ‘牛牛‘ # 實例變量,必須實例化才能調用 4 self.my() # 在構造函數中調用一次my方法,實例化時會自動執行 5 def my(self): # 實例方法,必須實例化才能調用 6 self.sex = ‘男‘ # 實例變量,必須實例化才能調用 7 8 Zll = Baby() 9 print(Zll.name) # 實例化時構造函數自動執行 10 print(Zll.sex) # 實例化時自動調用了一次my函數
(2)實例化後,先調用一次my函數,再使用my函數下的實例變量,如下:
1 class Baby(object): 2 def __init__(self): # 構造函數,類在實例化的時候自動執行的函數 3 self.name = ‘牛牛‘ # 實例變量,必須實例化才能調用 4 def my(self): # 實例方法,必須實例化才能調用 5 self.sex = ‘男‘ # 實例變量,必須實例化才能調用 6 7 Zll = Baby() 8 print(Zll.name) # 實例化時構造函數自動執行 9 Zll.my() # 調用一次my方法 10 print(Zll.sex)
2、析構函數
實例被銷毀的時候執行,但不是必須的。
一般可用於測試用例執行完畢後的關閉連接、關閉數據庫、刪除測試數據等操作。
例子:操作Mysql數據庫
1 import pymysql 2 class MyDb(object): 3 def __init__(self,host,user,db,passwd, 4 port=3306,charset=‘utf8‘): # 構造函數 5 try: 6 self.conn = pymysql.connect( 7 host=host,user=user,passwd=passwd,port=port,db=db,charset=charset, 8 autocommit=True # 自動提交 9 ) 10 except Exception as e: 11 print(‘數據庫連接失敗!:%s‘%e) 12 else: 13 self.cur = self.conn.cursor(cursor=pymysql.cursors.DictCursor) 14 15 def __del__(self): # 析構函數,實例被銷毀的時候執行 16 self.cur.close() 17 self.conn.close() 18 print(‘數據庫連接關閉‘) 19 20 def ex_sql(self,sql): 21 try: 22 self.cur.execute(sql) 23 except Exception as e: 24 print(‘sql語句有問題:%s‘%sql) 25 else: 26 self.res = self.cur.fetchall() 27 return self.res 28 29 my = MyDb(‘118.24.3.40‘,‘jxz‘,‘jxz‘,‘123456‘) 30 my.ex_sql(‘select * from stu;‘) 31 print(my.res) # 可以用實例屬性取值 32 print(my.ex_sql(‘select * from stu;‘)) # 也可以用實例方法的返回值 33 print(‘我是最後一行代碼‘) # 執行完最後這行代碼後再執行析構函數
五、私有變量&私有方法
出了類以後就不能再使用的變量和方法,被稱為私有變量、私有方法。
有些重要的信息不想被調用,可以加兩個下劃線"__",將變量或者方法變為私有。
1 class Redis(object): 2 def __init__(self): 3 self.__host = ‘127.0.0.1‘ # 私有變量 4 self.port = 6379 5 def __close(self): # 私有方法 6 print(‘私有方法‘) 7 def open(self): 8 print(‘實例方法‘) 9 self.__close() # 類內部可以調用私有方法 10 11 m = Redis() 12 print(m.__host) # 報錯,沒有__host這個屬性,私有變量出類後不能再使用 13 print(m.port) # 打印出:6379 14 m.open() # 打印出:實例方法,私有方法 15 m.__close() # 報錯,沒有這個方法,,私有方法出類後不能再使用
例子:操作Redis數據庫
1 # 封裝redis操作類 2 import redis 3 class My(object): 4 def __init__(self): 5 self.__host = ‘127.0.0.1‘ # 私有變量 6 self.__port = 6379 # 私有變量 7 self.__passwd = ‘‘ # 私有變量 8 self.__db = 1 # 私有變量 9 try: 10 self.r = redis.Redis(host=self.__host,port=self.__port,password=self.__passwd,db=self.__db) 11 except Exception as res: 12 print(‘redis連接失敗..【%s】‘%res) 13 # 這時並不會去連redis 14 # 所以捕捉不到數據庫連接失敗的異常,這裏寫的捕捉異常沒有意義,可以不寫 15 def __close(self): # 私有方法 16 print(‘close‘) 17 18 def str_get(self,name): # str類型get 19 res = self.r.get(name) 20 if res: 21 return res.decode() 22 return None 23 24 def str_set(self,name,value,time=None): # str類型set 25 self.r.set(name,value,time) 26 27 def str_delete(self,name): # str類型的刪除key 28 # res = self.r.get(name) 29 res = self.r.exists(name) # 判斷redis數據庫是否存在name這個key 30 if res: 31 self.r.delete(name) 32 print(‘刪除成功‘) 33 else: 34 print(‘%s不存在‘%name) 35 36 def hash_get(self,name,key): # hash類型獲取單個key 37 res = self.r.hget(name,key) 38 if res: 39 return res.decode() 40 return None 41 42 def hash_set(self,name,key,value): # hash類型set 43 self.r.hset(name,key,value) 44 45 def hash_getall(self,name): # hash類型獲取key裏面的所有數據 46 dic = {} 47 res = self.r.hgetall(name) 48 if res: 49 for key,value in res.items(): 50 dic[key.decode()] = value.decode() 51 return dic 52 return None 53 54 def hash_del(self,name,key): # 刪除某個hash裏面小key 55 res = self.r.hget(name,key) 56 if res: 57 self.r.hdel(name,key) 58 print(‘刪除成功‘) 59 else: 60 print(‘%s不存在‘%name) 61 62 def clean_redis(self): # 清理redis 63 # self.r.flushdb() # 清空redis數據庫 64 res = self.r.keys() 65 for key in res: 66 self.r.delete(key) 67 print(‘清空redis數據庫成功‘) 68 69 m = My() 70 m.str_set(‘Kity‘,‘famale‘) 71 print(m.str_get(‘Kity‘)) 72 m.str_delete(‘Kity‘) 73 m.hash_set(‘toy‘,‘car‘,‘red‘) 74 m.hash_set(‘toy‘,‘ball‘,‘blue‘) 75 print(m.hash_get(‘toy‘,‘car‘)) 76 print(m.hash_get(‘toy‘,‘car‘)) 77 m.hash_del(‘toy‘,‘car‘) 78 m.clean_redis()
Python學習之==>面向對象編程