Python面向對象的理解

python中一切皆爲對象,類型的本質就是類!Python常用的數據結構如:List、tuple、dict、array、DataFrame或者Series等等都是類,類的實例就是對象。甚至連fp=open(’…/data/zhou.txt’)的句柄fp都可以作爲(變量)對象傳入函數。一切就是這麼神奇!爲了提高代碼的複用性和更好的封裝函數或變量的作用域,使用類再做一次封裝會有用很多。

1.Python中self等價於Java中的this,this只是本類。
2.一個類佔有一個獨立的空間,類中的屬性叫做類變量,類中的函數,叫做類的方法。類(Class):也可以成爲類對象。類對象中包含了一批實例對象共有的屬性和方法。
3.既然是面向對象,自然滿足面向對象的幾大特性:抽象、封裝、繼承、多態(很好的解決了應用程序函數同名問題)
4.面向過程和麪向對象:
A.面向過程的程序設計的核心是過程(流水線式思維),過程即解決問題的步驟,面向過程的設計就好比精心設計好一條流水線,考慮周全什麼時候處理什麼東西。
a.優點是:極大的降低了寫程序的複雜度,只需要順着要執行的步驟,堆疊代碼即可。
b.缺點是:一套流水線或者流程就是用來解決一個問題,代碼牽一髮而動全身。
c.應用場景:一旦完成基本很少改變的場景,著名的例子有Linux內核,git,以及Apache HTTP Server等。

B.面向對象=對象+消息。
a.優點是:面向對象編程可以使程序的維護和擴展變得更簡單,並且可以大大提高程序開發效率 ,另外,基於面向對象的程序可以使它人更加容易理解你的代碼邏輯,從而使團隊開發變得更從容。對某一個對象單獨修改,會立刻反映到整個體系中,如對遊戲中一個人物參數的特徵和技能修改都很容易。
b.缺點:可控性差,無法向面向過程的程序設計流水線式的可以很精準的預測問題的處理流程與結果,面向對象的程序一旦開始就由對象之間的交互解決問題

注:個人理解,兩者最大的差異是一個通過函數的流水線式直接交互;一個封裝成類後,通過類的實例=對象,按照對象.屬性或對象.方法來實現交互!實現同一功能時,本質上異曲同工。

例子1:super()繼承的使用

Python面向對象編程
# 創建父類學校成員SchoolMember
class SchoolMember:
    def __init__(self,name,age):
        self.name=name
        self.age=age
    def tell(self):
        print('Name:"{}",Age:"{}"'.format(self.name,self.age),end=" ")
class Teacher(SchoolMember): #python中的繼承機制,這裏可以是多繼承;java中只能是單繼承,但是可以迭代繼承多個嵌套封裝類
    def __init__(self,name,age,salary):
        SchoolMember.__init__(self,name,age)
        self.salary=salary
    def tell(self):
        SchoolMember.tell(self)
        print('salary:"{}"'.format(self.salary))
class Student(SchoolMember):
    def __init__(self,name,age,score):
        #SchoolMember.__init__(self, name, age)#這裏與super()是等價的。
        super().__init__(name, age)
        self.score=score
    def tell(self):
        #SchoolMember.tell(self)
        super().tell()
        print('score: {}'.format(self.score))
   
teacher1 = Teacher("John", 44, "$60000")
student1 = Student("Mary", 12, 99)
teacher1.tell()  # 打印 Name:"John" Age:"44" Salary: $60000
student1.tell()  # Name:"Mary" Age:"12" score: 99

例子2:面向對象中對象的交互操作

#面向對象中對象的交互操作:
class Person:  # 定義一個人類
    role = 'person'  # 人的角色屬性都是人
    def __init__(self, name, aggressivity, life_value):
        self.name = name  # 每一個角色都有自己的暱稱;
        self.aggressivity = aggressivity  # 每一個角色都有自己的攻擊力;
        self.life_value = life_value  # 每一個角色都有自己的生命值;
    def attack(self,dog):  
        # 人可以攻擊狗,這裏的狗也是一個對象。
        # 人攻擊狗,那麼狗的生命值就會根據人的攻擊力而下降
        dog.life_value -= self.aggressivity
        print(dog.__class__,dog.name,'生命值減少',self.aggressivity,'還剩餘生命值: ',dog.life_value)

egg = Person('egon',10,1000)
print(egg.name)
print(egg.aggressivity)
print(egg.life_value)
print(egg.attack)
class Dog:  # 定義一個狗類
    role = 'dog'  # 狗的角色屬性都是狗
    def __init__(self, name,  aggressivity, life_value):
        self.name = name  # 每一隻狗都有自己的暱稱;
        self.aggressivity = aggressivity  # 每一隻狗都有自己的攻擊力;
        self.life_value = life_value  # 每一隻狗都有自己的生命值;
    def bite(self,dog):
        # 狗可以咬人,這裏的狗也是一個對象。
        # 狗咬人,那麼人的生命值就會根據狗的攻擊力而下降
        dog.life_value -= self.aggressivity
        print(dog.__class__,dog.name,'生命值減少',self.aggressivity,'還剩餘生命值: ',dog.life_value)

dog = Dog('哈士奇',35,1000)  #創造了一隻實實在在的狗ha2
print(dog.life_value)         #看看dog的生命值
egg.attack(dog)               #egg打了dog一下
print(dog.life_value)         #dog掉了10點血
#狗咬人一下
print(egg.life_value)         #看看egg的生命值
dog.bite(egg)               #dog打了egg一下
print(egg.life_value) 

例子3:面向對象的組合用法

#面向對象的組合用法
class Weapon:
    def prick(self, obj):  # 這是該裝備的主動技能,扎死對方
        obj.life_value -= 500  # 假設攻擊力是500
        print('prick後生命力爲:',obj.life_value)
class Person:  # 定義一個人類
    role = 'person'  # 人的角色屬性都是人
    def __init__(self, name,life_value):
        self.name = name  # 每一個角色都有自己的暱稱;
        self.weapon = Weapon()  # 給角色綁定一個武器; 屬性值=另一個類對象的實例   
        self.life_value=life_value
egg = Person('egon',1000)
egg.weapon.prick(egg) 
#egg組合了一個武器的對象,可以直接egg.weapon來使用組合類中的所有方法

#基礎的對象屬性和方法的調用
class Myclass: 
    @classmethod#類裝飾器 
    def foo2(self): 
        print(id(self),'foo2') 
print(id(Myclass)) #類對象,直接可以調用,不需要實例化。這句說明了類也是有存儲空間的 
Myclass.foo2() #類方法,直接可以調用,不需要實例化對象 

class Myclass:
  @staticmethod#靜態方法
  def foo3():   #沒有self參數
    print('foo3')
 
Myclass.foo3()   #通過類調用
a=Myclass()
a.foo3()       #通過實例調用

5.public,protected和private類型的規定:
單下劃線、雙下劃線、頭尾雙下劃線說明

實例對象無法訪問私有的類變量,但是可以添加同名的私有實例變量。_foo_(): 定義的是特列方法,類似 _init_() 之類的。

_foo: 以單下劃線開頭的表示的是 protected 類型的變量,即保護類型只能允許其本身與子類進行訪問,不能用於 from module import *

__foo: 雙下劃線的表示的是私有類型(private)的變量, 只能是允許這個類本身進行訪問了。

例子1

__all__=["default_age","default","Moniter"] 
# __all__變量,設置導入模塊import*時將會自動引入的函數、變量、類 
default_age = 24 #定義模塊變量 
def set_default_age(age=26): 
    #定義模塊函數 
    print("默認年齡爲"+str(age)+"歲") 
class Parent(object): 
    #定義模塊類。()內指定基類,當是object可以省略不寫 
    print("定義了Student類") #定義類時就會執行內部的執行語句 
    default_name='student' #定義一個類變量(不可修改變量) 
    default_arr = [] #定義一個類變量(可修改變量) 
    __default_age=12 #函數名或屬性名前面爲雙下劃線表示成員私有。只能在當前類或對象空間內使用。在存儲時是存儲成_Parent__default_age 
    def __init__(self, name1='student1',age1=13): 
        #init是構造函數,self代表類的實例對象。參數可以設置默認值。 
        self.name=name1 #自動新增兩個實例變量 
        self.age=age1 
        print("基類構造函數設置了"+self.name) 
    def getname(self): #實例方法,函數名爲引用變量,可以進行賦值,即變更函數體。函數引用變量調用時使用(),不帶括號代表變量。getname代表函數引用變量,getname()代表代表調用函數 
        print('基類讀取名稱'+self.name) 
        return self.name 
    def setname(self,name1): 
        self.name=name1 
        print("基類設置名稱"+self.name) 
    @staticmethod # @staticmethod聲明函數爲靜態方法,通過類對象和實例對象直接調用 
    def info(): #靜態方法就像類外函數一樣。如果函數內需要讀寫類變量,需要使用Parent.default_name 
        print("派生類的靜態函數"+Parent.default_name) 
    @classmethod # @classmethod聲明函數爲類方法,第一個參數是能是類對象的引用,可以通過類或者實例直用 
    def setsex(cls): # 類方法self表示對類的引用 
        self.sex = '男' # 添加類變量 
        print("派生類設置性別" + self.sex) # 派生類繼承了基類的類變量和類方法和實例方法(沒有實例變量,因爲實例變量是在實例以後才存在的) 
class Child(Parent): #生成派生類,可以多重繼承,但是應儘量避免。繼承後會包含基類的函數和特性。 
    def __init__(self,name1="child"): #派生類構造函數不會自動調用基類構造函數 
        self.name = name1 #在當前實例對象中新增name實例變量 
        Parent.__init__(self) #兩種方法,調用超類的構造函數。基類中就修改了name實例變量,新增了age實例變量 # super(Child,self).__init__(name1) 
        print("派生類構造函數"+self.name) #這裏讀取的就是最後一次對name的修改(基類中對他的修改) 
    def setname(self, name1): #重寫基類中的方法。其實是在派生類中添加了一個新方法,因此在查找此函數時就必用向上查找了。 
        self.name = "新"+name1 
        print("基類設置名稱" + self.name) 
    def getage(self): #派生類添加新的實例方法 
        print("派生類讀取年齡"+str(self.__default_age)) #派生類是無法讀取基類的私有類變量的。因此這句話會報錯 
print('Peopeo類開始運行') #導入模塊或執行模塊都會執行函數 # 當一個module被執行時,moduel.__name__的值將是"__main__",而當一個 module被其它module引用時,module.__name__將是module自己的名字 
if __name__=="__main__": #只有在執行當前模塊時纔會運行此函數 
    set_default_age()

6.Python內置方法,本質上也是類及類的實例:

""" import People=將People.py文件導入;等價的寫法是將People.py內部的屬性和類和方法重新封裝成一個總的類,即:People類。"""
class People:
    __all__=["default_age","default","Moniter"] 
    # __all__變量,設置導入模塊import*時將會自動引入的函數、變量、類 
    default_age = 24 #定義模塊變量 
    def set_default_age(age=26): 
        #定義模塊函數 
        print("默認年齡爲"+str(age)+"歲") 
    class Parent(object): 
        #定義模塊類。()內指定基類,當是object可以省略不寫 
        print("定義了Student類") #定義類時就會執行內部的執行語句 
        default_name='student' #定義一個類變量(不可修改變量) 
        default_arr = [] #定義一個類變量(可修改變量) 
        __default_age=12 #函數名或屬性名前面爲雙下劃線表示成員私有。只能在當前類或對象空間內使用。在存儲時是存儲成_Parent__default_age 
        def __init__(self, name1='student1',age1=13): 
            #init是構造函數,self代表類的實例對象。參數可以設置默認值。 
            self.name=name1 #自動新增兩個實例變量 
            self.age=age1 
            print("基類構造函數設置了"+self.name) 
        def getname(self): #實例方法,函數名爲引用變量,可以進行賦值,即變更函數體。函數引用變量調用時使用(),不帶括號代表變量。getname代表函數引用變量,getname()代表代表調用函數 
            print('基類讀取名稱'+self.name) 
            return self.name 
        def setname(self,name1): 
            self.name=name1 
            print("基類設置名稱"+self.name) 
        @staticmethod # @staticmethod聲明函數爲靜態方法,通過類對象和實例對象直接調用 
        def info(): #靜態方法就像類外函數一樣。如果函數內需要讀寫類變量,需要使用Parent.default_name 
            print("派生類的靜態函數"+Parent.default_name) 
        @classmethod # @classmethod聲明函數爲類方法,第一個參數是能是類對象的引用,可以通過類或者實例直用 
        def setsex(self): # 類方法self表示對類的引用 
            self.sex = '男' # 添加類變量 
            print("派生類設置性別" + self.sex) # 派生類繼承了基類的類變量和類方法和實例方法(沒有實例變量,因爲實例變量是在實例以後才存在的) 
    class Child(Parent): #生成派生類,可以多重繼承,但是應儘量避免。繼承後會包含基類的函數和特性。 
        def __init__(self,name1="child"): #派生類構造函數不會自動調用基類構造函數 
            self.name = name1 #在當前實例對象中新增name實例變量 
            Parent.__init__(self) #兩種方法,調用超類的構造函數。基類中就修改了name實例變量,新增了age實例變量 # super(Child,self).__init__(name1) 
            print("派生類構造函數"+self.name) #這裏讀取的就是最後一次對name的修改(基類中對他的修改) 
        def setname(self, name1): #重寫基類中的方法。其實是在派生類中添加了一個新方法,因此在查找此函數時就必用向上查找了。 
            self.name = "新"+name1 
            print("基類設置名稱" + self.name) 
        def getage(self): #派生類添加新的實例方法 
            print("派生類讀取年齡"+str(self.__default_age)) #派生類是無法讀取基類的私有類變量的。因此這句話會報錯 
    print('Peopeo類開始運行') #導入模塊或執行模塊都會執行函數 # 當一個module被執行時,moduel.__name__的值將是"__main__",而當一個 module被其它module引用時,module.__name__將是module自己的名字 
    #if __name__=="__main__": #只有在執行當前模塊時纔會運行此函數 
    #    set_default_age()

# 只有有__init__.py文件的文件夾才能是package,才支持引入
#import People    #引用此模塊,就相當於將此模塊在此處展開。
#python中一切變量都是引用變量,指向的對象包含不可變量(整型、字符串、元組)和可變量。
# 因此如果修改了不可變量,實質是新開一個空間,修改引用指向。如果修改可變量,就是保持引用指向,在原位置修改數據


# #調用模塊變量、模塊函數
People.set_default_age(People.default_age)
print('=============實例化===================')
#實例化對象,開闢新內存,保留對類的引用,但是不復制屬性和方法。同時調用init初始化,
#因此實例變量可以訪問類變量、類方法、靜態方法、實例方法。以及自己創建的實例變量。
# 類對象無法訪問實例變量。因爲並沒有一個從類對象到實例對象的指針

#實例變量可以讀取的東西:類的公有屬性和方法,自身的所有屬性和方法。
#實例變量可以修改的東西:類對象的引用變量指向的數據,和自身的所有屬性和方法
#實例變量不能修改的東西:類對象的引用變量的指向(包含屬性和方法)
parent1 = People.Parent('student1')   #實例化,盜用初始化函數,創建了實例變量name和age
print('基類對象:',People.Parent.__dict__)     #打印對象的自有屬性
print('基類實例對象:',parent1.__dict__)    #這一步可以看出實例化沒有將類中的屬性和方法引用複製到實例對象空間中。
print('==============實例對象讀寫數據==================')
parent1.default_name   #實例可以訪問類變量,因爲變量沿原型鏈的查找
parent1.default_name = 'Student'   #實例無法修改類變量(引用變量)的指向,所以這個是在實例對象中添加了一個實例變量
parent1.default_arr.append(1)   #實例可以修改類變量指向的數據內容
parent1.default_arr = [1,2]    #實例無法修改類變量(引用變量)的指向,所以這個是在實例對象中添加了一個實例變量
# print(parent1.__default_age)   #實例對象不能訪問類對象的私有類變量
parent1.__default_age=14     #爲實例對象添加實例變量(在執行時添加的不再是私有變量)
print(parent1.__default_age)  #實例對象可以訪問到自身的所有屬性和方法

print('基類對象:',People.Parent.__dict__)
print('基類實例對象:',parent1.__dict__)
print('=============基類對象讀寫數據===================')
#類對象可以訪問和修改的內容:類的屬性和方法
People.Parent.default_name='Student'   #修改類變量的指向
People.Parent.default_arr=[11]          #修改類變量的指向
People.Parent.default_arr.append(12)    #修改類變量指向的內容
People.Parent.__default_age=14          #通過類名無法修改私有類變量,因爲在存儲中私有變量的存儲名爲_Parent__default_age。python並不建議修改私有變量,雖然可以通過這個名稱修改變量值
print('基類對象:',People.Parent.__dict__)
print('基類實例對象:',parent1.__dict__)

print('=============繼承===================')
#派生類,開闢新內存,保留對基類的引用,但是不復制屬性和方法。
# 因此派生類對象可以訪問基類變量、基類方法、基類靜態方法、基類實例方法。以及自己創建的派生類屬性和方法。基類對象無法訪問派生類對象。因爲並沒有一個從基類對象到派生類對象的指針

#派生類對象可以讀取的東西:基類的公有和保護屬性和方法,自身的所有屬性和方法。
#派生類對象可以修改的東西:基類對象的引用變量指向的數據,和自身的所有屬性和方法
#派生類不能修改的東西:基類對象的引用變量的指向(包含屬性和方法)

print('基類對象:',People.Parent.__dict__)     #打印對象的自有屬性
print('派生類對象:',People.Child.__dict__)    #這一步可以看出實例化沒有將類中的屬性和方法引用複製到實例對象空間中。
print('=============派生類對象讀寫數據===================')
People.Child.default_name   #派生類對象可以訪問基類的類變量,因爲變量沿原型鏈的查找
People.Child.default_name = 'child'   #派生類對象無法修改基類的類變量(引用變量)的指向,所以這個是在派生類對象中添加了一個類變量
People.Child.default_arr.append(1)   #派生類對象可以修改基類的類變量指向的數據內容
People.Child.default_arr = [1,2]    #派生類對象無法修改基類的類變量(引用變量)的指向,所以這個是在派生類對象中添加了一個類變量
# print(People.Child.__default_age)   #派生類對象不能訪問基類對象的私有類變量
People.Child.__default_age=14     #爲派生類對象添加類變量(在執行時添加的不再是私有變量)
print(People.Child.__default_age)  #類對象可以訪問到自身的所有屬性和方法

print('基類對象:',People.Parent.__dict__)
print('派生類對象:',People.Child.__dict__)
print('=============派生類的實例對象讀寫數據===================')
#基類的實例對象和基類的關係等同於派生類的實例對象和派生類的關係
# 派生類的初始化函數調用不會自動調用基類的初始化函數
child1 = People.Child()  #調用類的初始化函數,實例化一個對象,這裏在派生類的實例對象中添加了name和age屬性
child1.setname('child')  #調用派生類對象中的重寫或繼承的方法
# child1.getage()  #調用派生類對象添加的方法,方法訪問了基類的私有變量,會報錯
print('派生類對象:',People.Child.__dict__)
print('派生類實例對象:',child1.__dict__)

print('==============靜態方法==================')
parent1.info()         #調用靜態函數,方法1
People.Parent.info()   #調用靜態函數,方法2
print('=============類方法===================')
# print(parent1.sex)   #類對象和實例對象中不存在sex,所以無法查找到,訪問出錯
parent1.setsex()    #調用類方法,在類對象中添加類變量
print(parent1.sex)  #通過實例對象訪問類變量

print('基類對象:',People.Parent.__dict__)
print('基類實例1對象:',parent1.__dict__)


print('===========原型鏈查詢=====================')
print(issubclass(People.Child,People.Parent)) # 布爾函數(Child,Parent)判斷一個類是另一個類的子類或者子孫類,語法:issubclass(sub,sup)
print(isinstance(child1, People.Parent))  #布爾函數如果obj是Class類的實例對象或者是一個Class子類的實例對象則返回true。

注:針對博客裏面很多代碼直接粘貼會大變樣,建議:使用右鍵>查看網頁源代碼>發現代碼格式被很好的保存下來了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章