一、面向對象編程與面向過程編程對比
1、面向過程編程:核心過程二字,過程指的是解決問題的步驟,既先干什么、再干什么、后干什么,基於該思想的編程就好比在生產一條流水線,是一種機械式的思維方式。
優點:復雜的問題流程化進而簡單化
缺點:可擴展性差
2、面向對象編程:核心是對象二字,對象是技能與特征的結合體,基於該思想編寫程序就好比在創造一個世界,世界是由一個個對象組成的,在上帝眼里任何存在的事物都是對象,任何不存在的事物也都可以創造出來,是一種上帝式的思維方式
優點:可擴展性強
缺點:編程的復雜度要高於面向過程
二、類與對象
對象:對象是技能與特征的結合體
類:對象相同特征與技能的結合體
對象是具體存在的事物,而類則是抽象出來的概念,站在不同角度總結出來的類與對象是不同的
在現實世界中,現有一個個具體存在的對象,然后隨着人類文明的發展才總結出類的概念

#在現實世界中,站在老男孩學校的角度:先有對象,再有類 對象1:李坦克 特征: 學校=oldboy 姓名=李坦克 性別=男 年齡=18 技能: 學習 吃飯 睡覺 對象2:王大炮 特征: 學校=oldboy 姓名=王大炮 性別=女 年齡=38 技能: 學習 吃飯 睡覺 對象3:牛榴彈 特征: 學校=oldboy 姓名=牛榴彈 性別=男 年齡=78 技能: 學習 吃飯 睡覺 現實中的老男孩學生類 相似的特征: 學校=oldboy 相似的技能: 學習 吃飯 睡覺
在程序中,是先定義出類的概念后調用類來產生對象

#在程序中,務必保證:先定義(類),后使用(產生對象) PS: 1. 在程序中特征用變量標識,技能用函數標識 2. 因而類中最常見的無非是:變量和函數的定義 #程序中的類 class OldboyStudent: school='oldboy' def learn(self): print('is learning') def eat(self): print('is eating') def sleep(self): print('is sleeping') #注意: 1.類中可以有任意python代碼,這些代碼在類定義階段便會執行 2.因而會產生新的名稱空間,用來存放類的變量名與函數名,可以通過OldboyStudent.__dict__查看 3.對於經典類來說我們可以通過該字典操作類名稱空間的名字(新式類有限制),但python為我們提供專門的.語法 4.點是訪問屬性的語法,類中定義的名字,都是類的屬性 #程序中類的用法 .:專門用來訪問屬性,本質操作的就是__dict__ OldboyStudent.school #等於經典類的操作OldboyStudent.__dict__['school'] OldboyStudent.school='Oldboy' #等於經典類的操作OldboyStudent.__dict__['school']='Oldboy' OldboyStudent.x=1 #等於經典類的操作OldboyStudent.__dict__['x']=1 del OldboyStudent.x #等於經典類的操作OldboyStudent.__dict__.pop('x') #程序中的對象 #調用類,或稱為實例化,得到對象 s1=OldboyStudent() s2=OldboyStudent() s3=OldboyStudent() #如此,s1、s2、s3都一樣了,而這三者除了相似的屬性之外還各種不同的屬性,這就用到了__init__ #注意:該方法是在對象產生之后才會執行,只用來為對象進行初始化操作,可以有任意代碼,但一定不能有返回值 class OldboyStudent: ...... def __init__(self,name,age,sex): self.name=name self.age=age self.sex=sex ...... s1=OldboyStudent('李坦克','男',18) #先調用類產生空對象s1,然后調用OldboyStudent.__init__(s1,'李坦克','男',18) s2=OldboyStudent('王大炮','女',38) s3=OldboyStudent('牛榴彈','男',78) #程序中對象的用法 #執行__init__,s1.name='牛榴彈',很明顯也會產生對象的名稱空間 s2.__dict__ {'name': '王大炮', 'age': '女', 'sex': 38} s2.name #s2.__dict__['name'] s2.name='王三炮' #s2.__dict__['name']='王三炮' s2.course='python' #s2.__dict__['course']='python' del s2.course #s2.__dict__.pop('course')
__init__方法

#方式一、為對象初始化自己獨有的特征 class People: country='China' x=1 def run(self): print('----->', self) # 實例化出三個空對象 obj1=People() obj2=People() obj3=People() # 為對象定制自己獨有的特征 obj1.name='egon' obj1.age=18 obj1.sex='male' obj2.name='lxx' obj2.age=38 obj2.sex='female' obj3.name='alex' obj3.age=38 obj3.sex='female' # print(obj1.__dict__) # print(obj2.__dict__) # print(obj3.__dict__) # print(People.__dict__) #方式二、為對象初始化自己獨有的特征 class People: country='China' x=1 def run(self): print('----->', self) # 實例化出三個空對象 obj1=People() obj2=People() obj3=People() # 為對象定制自己獨有的特征 def chu_shi_hua(obj, x, y, z): #obj=obj1,x='egon',y=18,z='male' obj.name = x obj.age = y obj.sex = z chu_shi_hua(obj1,'egon',18,'male') chu_shi_hua(obj2,'lxx',38,'female') chu_shi_hua(obj3,'alex',38,'female') #方式三、為對象初始化自己獨有的特征 class People: country='China' x=1 def chu_shi_hua(obj, x, y, z): #obj=obj1,x='egon',y=18,z='male' obj.name = x obj.age = y obj.sex = z def run(self): print('----->', self) obj1=People() # print(People.chu_shi_hua) People.chu_shi_hua(obj1,'egon',18,'male') obj2=People() People.chu_shi_hua(obj2,'lxx',38,'female') obj3=People() People.chu_shi_hua(obj3,'alex',38,'female') # 方式四、為對象初始化自己獨有的特征 class People: country='China' x=1 def __init__(obj, x, y, z): #obj=obj1,x='egon',y=18,z='male' obj.name = x obj.age = y obj.sex = z def run(self): print('----->', self) obj1=People('egon',18,'male') #People.__init__(obj1,'egon',18,'male') obj2=People('lxx',38,'female') #People.__init__(obj2,'lxx',38,'female') obj3=People('alex',38,'female') #People.__init__(obj3,'alex',38,'female') # __init__方法 # 強調: # 1、該方法內可以有任意的python代碼 # 2、一定不能有返回值 class People: country='China' x=1 def __init__(obj, name, age, sex): #obj=obj1,x='egon',y=18,z='male' # if type(name) is not str: # raise TypeError('名字必須是字符串類型') obj.name = name obj.age = age obj.sex = sex def run(self): print('----->', self) # obj1=People('egon',18,'male') obj1=People(3537,18,'male') # print(obj1.run) # obj1.run() #People.run(obj1) # print(People.run)
類是一系列對象相同特征與技能的結合體,既類體中最常見的就是變量與函數體,但其實類體中是可以存在任意python代碼的,
類體中會在定義階段立即執行,會產生一個類的名稱空間,用來將類體代碼執行過程中產生的名字都丟進去
總結:①類本質就是一個名稱空間,或者說就是一個用來存放變量與函數的容器,
②類的用途之一就是當做名稱空間,從其內部取出名字來使用
③類的用途之二是用來調用類產生對象
調用類產生對象:調用類的過程稱之為類的實例化,調用類的返回值稱之為類的一個對象

調用類發生了:
1、產生一個空對象
2、觸發類中的__init__方法,將對象連同調用類括號內指定的參數一同傳入__init__
類中定義的變量是類的數據屬性,類可以用,對象也可以用,大家都指向同一個地址,類變量值一旦改變,所有對象都跟着改變
類中定義的函數是類的函數屬性,類可以用,類來調用就是一個普通的函數,但其實類中定義的函數就是給對象用的,而且是綁定給對象用的
綁定方法:指向類的函數(特殊之處是綁定給誰就應該由誰調用)
類的函數:該傳幾個參數就傳幾個參數
三大特性之繼承:
利用繼承能來解決類與類之間的代碼冗余問題
繼承是一種新建類的方法,新建的類稱之為子類(派生類),被繼承的類稱之為父類、基類、超類
繼承的特性:子類可以遺傳、重用父類的屬性
python中繼承類的特點:
1、在python中一個子類可以同時繼承多個父類
2、在繼承背景下說,python中類分為兩種:新式類和經典類
新式類:但凡繼承了object的類以及該類的子類都是新式類
python中一個類即便沒有顯示的繼承任何類,默認就會繼承object,即在python3中所有的類都是新式類
經典類:沒有繼承object的類以及該類的子類都是經典類
在單繼承的基礎下屬性查找的順序是:對象——》對象類——》父親——》父類。。。
子類中重用父類的屬性:
方式一:
在子類派生出的新方法中指名道姓的引用某一個類中的函數
與繼承無關,訪問的是父類中的函數沒有自動傳值功能
繼承解決的是類與類之間代碼冗余的問題,一定是一個類是另外一個類的子類
總結對象之間的相似之處得到類,總結類之間的相似之處得到的就是父類
多繼承背景下屬性查找的順序:對象--》對象的類--》按照從左往右的順序一個個的分支找下去
一旦出現菱形繼承問題,新式類與經典類在屬性查找上的區別是:
新式類:廣度優先查找,在最后一個分支查找頂級類
經典類:深度優先查找,在第一個分支就查找頂級類
mro()查找的順序:
在子類派生出的新的方法中重用父類功能的方式二
在子類中用super()方法
python2中:super(自己的類名,對象自己)
python3中:super()
調用super()方法會得到一個特殊的對象,該對象是專門用來引用父類中的屬性,完全參照mro()列表 訪問是綁定方法,有自動傳值的效果
組合:
組合指的是一個對象擁有一個屬性,該屬性的值屬於另外一個類的對象
組合通過一個對象添加屬性(屬性的值是另外一個類的對象)的方式,可以間接的將兩個類關聯、整合,從而減少類之間的代碼冗余問題
多態:
多態指的是同一事物的不同形態
在多態背景下,可以不用考慮對象具體的類型的前提下直接使用對象多態的精髓:統一
父類只是用來建立規范的,不能用來實例化的,更無需實現內部方法。
python崇尚鴨子類型
封裝:
裝:往容器、名稱空間內存入名字
封:代表存放於名稱空間的名字給藏起來,這種隱藏對外不對內
在類內定義的屬性前加__開頭(沒有__結尾)
總結:
1、__開頭的屬性實現的隱藏僅僅只是一種語法上的變形,並不會真正的限制類外部的訪問
2、該變形操作只在類定義階段檢測語法時發生一次,類定義階段之后新增的__開頭的屬性不會變形
3、如果父類不想讓子類覆蓋自己的屬性,可以在屬性前加__開頭
4、peoperty裝飾器是用來將類內的函數屬性偽裝為數據屬性
綁定方法與非綁定方法
一:綁定方法(綁定給誰,誰來調用就自動將它本身當作第一個參數傳入):
1. 綁定到類的方法:用classmethod裝飾器裝飾的方法。
為類量身定制
類.boud_method(),自動將類當作第一個參數傳入
(其實對象也可調用,但仍將類當作第一個參數傳入)
2. 綁定到對象的方法:沒有被任何裝飾器裝飾的方法。
為對象量身定制
對象.boud_method(),自動將對象當作第一個參數傳入
(屬於類的函數,類可以調用,但是必須按照函數的規則來,沒有自動傳值那么一說)
import settings class MySQL: def __init__(self,host,port): self.host=host self.port=port @classmethod def from_conf(cls): print(cls) return cls(settings.HOST,settings.PORT) print(MySQL.from_conf) #<bound method MySQL.from_conf of <class '__main__.MySQL'>> conn=MySQL.from_conf() conn.from_conf() #對象也可以調用,但是默認傳的第一個參數仍然是類
二:非綁定方法:用staticmethod裝飾器裝飾的方法
1. 不與類或對象綁定,類和對象都可以調用,但是沒有自動傳值那么一說。就是一個普通工具而已
注意:與綁定到對象方法區分開,在類中直接定義的函數,沒有被任何裝飾器裝飾的,都是綁定到對象的方法,可不是普通函數,對象調用該方法會自動傳值,而staticmethod裝飾的方法,不管誰來調用,都沒有自動傳值一說

import hashlib import time class MySQL: def __init__(self,host,port): self.id=self.create_id() self.host=host self.port=port @staticmethod def create_id(): #就是一個普通工具 m=hashlib.md5(str(time.time()).encode('utf-8')) return m.hexdigest() print(MySQL.create_id) #<function MySQL.create_id at 0x0000000001E6B9D8> #查看結果為普通函數 conn=MySQL('127.0.0.1',3306) print(conn.create_id) #<function MySQL.create_id at 0x00000000026FB9D8> #查看結果為普通函數
classmethod與staticmethod的區別

import settings class MySQL: def __init__(self,host,port): self.host=host self.port=port @staticmethod def from_conf(): return MySQL(settings.HOST,settings.PORT) # @classmethod #哪個類來調用,就將哪個類當做第一個參數傳入 # def from_conf(cls): # return cls(settings.HOST,settings.PORT) def __str__(self): return '就不告訴你' class Mariadb(MySQL): def __str__(self): return '<%s:%s>' %(self.host,self.port) m=Mariadb.from_conf() print(m) #我們的意圖是想觸發Mariadb.__str__,但是結果觸發了MySQL.__str__的執行,打印就不告訴你:
三、 元類
元類源自一句話,在python中一切皆對象,而對象都是由類實例化得到的,既然這樣那樣類也是對象,也是通過實例化得到的,內置類為type
調用關系是:
調用元類——》自定義的類
調用自定義的類——》自定義的對象
自定義類的三個關鍵組成部分
1 類名
2 類的基類們
3 類的名稱空間
class關鍵字的底層的工作原理
1、先拿到類名(oldboyteacher)
2、再拿到類的基類們:(object,)
3、拿到類的名稱空間(執行類體代碼,將產生的名字放到類的名稱空間也就是一個字典里,補充exec)
4、調用元類實例化得到自定義的類:、oldboyTeacher=type('oldboyTeacher',(object){})
實現代碼

class_name="OldboyTeacher" class_bases=(object,) class_dic={} class_body=''' school=self.shcool def __init__(self,...) ............. def score(......) ........... exec('x=1',{},class_dic) print(class_dic) OldboyTeacher_type(class_name,class_bases,class_dic)
自定義元類來控制類的產生:
但凡繼承了type的類才能稱之為自定義的元類,否則就是一個普通的類
對象之所以可以調用是因為對象有一個函數__call__
實例化發生的三件事:
1、產生一個空對象
2、執行__init__方法,完成對象初始化屬性的操作
3、返回初始化好的那個對象
自定義類來控制類的調用