面向對象編程知識點綜合


一、面向對象編程與面向過程編程對比

  1、面向過程編程:核心過程二字,過程指的是解決問題的步驟,既先干什么、再干什么、后干什么,基於該思想的編程就好比在生產一條流水線,是一種機械式的思維方式。

    優點:復雜的問題流程化進而簡單化

    缺點:可擴展性差

  2、面向對象編程:核心是對象二字,對象是技能與特征的結合體,基於該思想編寫程序就好比在創造一個世界,世界是由一個個對象組成的,在上帝眼里任何存在的事物都是對象,任何不存在的事物也都可以創造出來,是一種上帝式的思維方式

    優點:可擴展性強

    缺點:編程的復雜度要高於面向過程

二、類與對象

  對象:對象是技能與特征的結合體

  類:對象相同特征與技能的結合體

    對象是具體存在的事物,而類則是抽象出來的概念,站在不同角度總結出來的類與對象是不同的

    在現實世界中,現有一個個具體存在的對象,然后隨着人類文明的發展才總結出類的概念

#在現實世界中,站在老男孩學校的角度:先有對象,再有類
對象1:李坦克
    特征:
        學校=oldboy
        姓名=李坦克
        性別=男
        年齡=18
    技能:
        學習
        吃飯
        睡覺

對象2:王大炮
    特征:
        學校=oldboy
        姓名=王大炮
        性別=女
        年齡=38
    技能:
        學習
        吃飯
        睡覺

對象3:牛榴彈
    特征:
        學校=oldboy
        姓名=牛榴彈
        性別=男
        年齡=78
    技能:
        學習
        吃飯
        睡覺


現實中的老男孩學生類
    相似的特征:
        學校=oldboy
    相似的技能:
        學習
        吃飯
        睡覺
View Code

 

    在程序中,是先定義出類的概念后調用類來產生對象

#在程序中,務必保證:先定義(類),后使用(產生對象)
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')
View Code

  __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)
View Code

類是一系列對象相同特征與技能的結合體,既類體中最常見的就是變量與函數體,但其實類體中是可以存在任意python代碼的,

類體中會在定義階段立即執行,會產生一個類的名稱空間,用來將類體代碼執行過程中產生的名字都丟進去

總結:①類本質就是一個名稱空間,或者說就是一個用來存放變量與函數的容器,

     ②類的用途之一就是當做名稱空間,從其內部取出名字來使用

   ③類的用途之二是用來調用類產生對象

調用類產生對象:調用類的過程稱之為類的實例化,調用類的返回值稱之為類的一個對象

View Code

  調用類發生了:

    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> #查看結果為普通函數
View Code

 

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__的執行,打印就不告訴你:
View Code

三、 元類

  元類源自一句話,在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)
View Code

 

自定義元類來控制類的產生:

  但凡繼承了type的類才能稱之為自定義的元類,否則就是一個普通的類

  對象之所以可以調用是因為對象有一個函數__call__

  實例化發生的三件事:

    1、產生一個空對象

    2、執行__init__方法,完成對象初始化屬性的操作

    3、返回初始化好的那個對象

自定義類來控制類的調用

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM