面向對象的三大屬性


 

1,面向對象中的繼承表示的是類與類之間的關系(什么是什么的關系),在python3中,所有的類都會默認繼承object類,繼承了object類的所有類都是新式類,如果一個類沒有繼承任何父類,那么__bases__屬性就會顯示<class 'object'>。

2,繼承可以分為單繼承和多繼承。

# 單繼承
class Parent:pass
class Son(Parent):pass   # 繼承關系
# Son繼承了Parent
print(Son.__bases__)  # 內置的屬性
print(Parent.__bases__)  # 內置的屬性


# 多繼承
class Parent1:pass
class Parent2(Parent1):pass
class Parent3:pass
class Son(Parent2,Parent3):pass   # 繼承關系
print(Parent2.__bases__)
print(Son.__bases__)
View Code

3,對象使用名字的順序: 先找對象自己內存空間中的,再找對象自己類中的,再找父類中的。

class Animal:
    role = 'Animal'
    def __init__(self,name,hp,ad):
        self.name = name     # 對象屬性 屬性
        self.hp = hp         #血量
        self.ad = ad         #攻擊力
    def eat(self):
        print('%s吃葯回血了'%self.name)

class Person(Animal):
    r = 'Person'
    def attack(self,dog):   # 派生方法
        print("%s攻擊了%s"%(self.name,dog.name))

    def eat2(self):
        print('執行了Person類的eat方法')
        self.money = 100
        self.money -= 10
        self.hp += 10

class Dog(Animal):
    def bite(self,person):  # 派生方法
        print("%s咬了%s" % (self.name, person.name))

# 繼承中的init
alex = Person('alex',10,5)
print(Person.role)
print(alex.__dict__)
dog = Dog('teddy',100,20)
print(dog.__dict__)

# 繼承中的派生方法
alex = Person('alex',10,5)
dog = Dog('teddy',100,20)
alex.attack(dog)
dog.bite(alex)

# 繼承父類的方法:自己沒有同名方法
alex = Person('alex',10,5)
dog = Dog('teddy',100,20)
alex.eat2()
alex.eat()
dog.eat()
View Code
# class Animal:
#     def __init__(self,name,sex,kind):
#         self.name = name
#         self.sex = sex
#         self.kind = kind
#     def eat(self):
#         print('%s is eating'%self.name)
#
#     def drink(self):
#         print('%s is drinking'%self.name)
#
# class Cat(Animal):
#     def climb(self):
#         print('%s is climbing'%self.name)
#
# class Dog(Animal):
#     def watch_door(self):
#         print('%s is watching door'%self.name)

# 1.確認自己沒有init方法
# 2.看看有沒有父類
# 3.發現父類Animal有init
# 4.看着父類的init方法來傳參數
#tom = Cat('tom','公','招財貓')   # 實例化對象
# tom.eat = '貓糧'
# print(Cat.__dict__)   # Cat.__dict__ Cat類的命名空間中的所有名字
# print(tom.__dict__)   # tom.__dict__ 對象的命名空間中的所有名字
# tom.eat()   # 先找自己對象的內存空間 再找類的空間 再找父類的空間
# tom.climb()   # 先找自己的內存空間 再找類的空間
View Code

4,self.名字的時候,不要看self當前在哪個類里,要看這個self到底是誰的對象

class Parent:
    def func(self):
        print('in parent func')
    def __init__(self):
        self.func()

class Son(Parent):
    def func(self):
        print('in son func')

s = Son()
View Code

 5,object 類

class A:
#     '''
#     這是一個類
#     '''
    pass

a = A()
print(A.__dict__)  # 雙下方法 魔術方法
# 創建一個空對象
# 調用init方法    —— 調用了么? 調用了
# 將初始化之后的對象返回調用處
View Code

6,在單繼承中,如何給一個類的對象增加派生屬性呢?

詳見如下代碼:

class Animal:
    def __init__(self,name,hp,ad):
        self.name = name     # 對象屬性 屬性
        self.hp = hp         #血量
        self.ad = ad         #攻擊力
    def eat(self):
        print('eating in Animal')
        self.hp += 20

class Person(Animal):
    def __init__(self,name,hp,ad,sex):
        # Animal.__init__(self,name,hp,ad)
        #次為方法一
        # super(Person,self).__init__(name,hp,ad)
        #此為方法二
        super().__init__(name, hp, ad)
        #此為方法二簡寫(常用)
        # 在單繼承中,super負責找到當前類所在的父類,在這個時候不需要再手動傳self
        self.sex = sex      # 這個就是增加的派生屬性
        self.money = 100
    def attack(self,dog):   # 派生方法
        print("%s攻擊了%s"%(self.name,dog.name))
    def eat(self):                         # 重寫
        super().eat()  # 在類內 使用 super()方法找父類的方法
        print('eating in Person')
        self.money -= 50

class Dog(Animal):
    def __init__(self,name,hp,ad,kind):
        Animal.__init__(self,name,hp,ad)
        self.kind = kind    # 派生屬性
    def bite(self,person):  # 派生方法
        print("%s咬了%s" % (self.name, person.name))


alex = Person("zhangsan",100,10,"male")   # 實例化
#print(alex.__dict__)
# tg = Dog('到哥',100,50,'藏獒')
# print(tg.__dict__)
# 父類有eat 子類沒有
# alex.eat() # 找父類的
# 子類有eat 不管父類中有沒有
# alex.eat() # 找子類的
# 當子類中有,但是我想要調父類的
# Animal.eat(alex)          #指名道姓
# super(Person,alex).eat()  # super(子類名,子類對象)方法   —— 一般不用
# 子類父類都有eat方法,我想執行父類的eat和子類的eat
#alex.eat()  # 執行子類的eat
View Code

7,多繼承之鑽石繼承

對於多繼承中的新式類,尋找名字的順應該遵循廣度優先。類名.mro() 可以查詢廣度優先的遍歷順序。

例如下面的程序:

class A:
    def func(self):
        print('A')
class B(A):
    pass
    # def func(self):
    #     print('B')
class C(A):
    pass
    # def func(self):
    #     print('C')
class D(B):
    pass
    # def func(self):
    #     print('D')
class E(B,C):
    pass
    # def func(self):
    #     print('E')
class F(D,E):
    pass
    # def func(self):
    #     print('F')
f = F()
f.func()
print(F.mro())  # 查看廣度優先的遍歷順序,其遍歷順序為 F-D-E-B-C-A
View Code

【附】:pyrhon3 中的新式類遵循的廣度優先算法圖示

8,當遇到多繼承和super時,應遵循以下的情況:

(1)找到這個對象對應的類。

(2)將這個類的所有父類都找到畫成一個圖

(3)根據圖寫出廣度優先的順序

(4)再看代碼,看代碼的時候要根據廣度優先順序圖來找對應的super

例如以下程序:

class A:
    def func(self):
        print('A')
class B(A):
    def func(self):
        super().func()
        print('B')
class C(A):
    def func(self):
        super().func()
        print('C')
class D(B,C):
    def func(self):
        super().func()
        print('D')
d = D()
d.func()     #結果為 A-C-B-D
View Code

該程序分析過程如下:

【注】:supper(),在單繼承中就是單純的尋找父類,在多繼承中就是根據子節點所在圖的 mro 順序找尋下一個類。

9,python 2中的經典類的多繼承問題

【注】:經典類只在 python2 版本才存在,且必須不繼承object。它在遍歷的時候遵循深度優先算法,而且沒有mro方法和super()方法。在python2 的版本中,必須手動繼承object 類,才是新式類。它在遍歷的時候遵循廣度優先算法,在新式類中,有mro方法和super()方法,但是在2.X版本的解釋器中,必須傳參數(子類名,子類對象)。

【附】:python2 中的經典類中遵循的深度優先算法圖示

二:多態

1,什么是多態?

在java中,多態就是在一個類之下發展出來的多個類的對象都可以作為參數傳入這里。而在python中不需要刻意實現多態,因為python本身自帶多態效果。

2,鴨子類型

不是通過具體的繼承關系來約束某些類中必須有哪些方法名,而是通過一種約定俗成的概念來保證在多個類中相似的功能叫相同的名字。

class QQpay():
    def pay(self,money):
        print('使用qq支付了%s元'%money)

class Wechatpay():
    def pay(self,money):
        print('使用微信支付了%s元'%money)

def pay(pay_obj,money):
    pay_obj.pay(money)
a=QQpay()
pay(a,100)
View Code

三:封裝

1,什么是封裝?

封裝就是隱藏對象的屬性和實現細節,僅對外提供公共訪問方式。

2,私有靜態變量

首先定義一個私有的名字: 就是在私有的名字前面加兩條下划線 ,如  __N = 'aaa'

class A:
    __N = 'aaa'  # 靜態變量
    def func(self):
        print(A.__N)  # 在類的內部使用正常

a = A()
a.func()
print(A.__N)   # 在類的外部直接使用 報錯
View Code

 

ps:一個私有的名字 在存儲的過程中仍然會出現在  類名.__dict__中,所以我們仍然可以調用到。python對其的名字進行了修改: _類名__名字,只不過在類的外部調用 :需要“_類名__名字”去使用,在類的內部可以正常的使用名字,在類內 只要你的代碼遇到__名字,就會被python解釋器自動的轉換成_類名__名字。

3,私有屬性

class B:
    def __init__(self,name):
        self.__name = name
    def func(self):
        print('in func : %s'%self.__name)
b = B('alex')
print(b._B__name)
b.func()
View Code

4,私有方法

class C:
    def __wahaha(self):
        print('wahaha')
    def ADCa(self):
        self.__wahaha()
c = C()
c._C__wahaha()
c.ADCa()    #在內部調用私有方法
View Code

5,私有的名字不能被子類繼承

class D:
    def __func(self):      # '_D__func'
        print('in func')

class E(D):
    def __init__(self):
        self.__func()      # '_E__func'
e = E()

#該程序運行后會報錯,因為func方法被私有化后是不能被子類繼承的
View Code
class D:
    def __init__(self):
        self.__func()
    def __func(self):
        print('in D')

class E(D):
    def __func(self):
        print('in E')
e = E()
# 私有的名字,在類內使用的時候,就是會變形成_該類名__方法名
# 以此為例 :沒有雙下換線會先找E中的func
# 但是有了雙下划線,會在調用這個名字的類D中直接找_D__func
View Code

6,私有的用處

(1)當一個方法不想被子類繼承的時候。

(2)有些屬性或者方法不希望從外部被調用,只想提供給內部的方法使用

class Room:
    def __init__(self,name,price,length,width,hight):
        self.name = name
        self.price = price
        self.__length = length
        self.__width = width
        self.hight = hight
    def area(self):
        return self.__length*self.__width

r = Room('ads',22,10,10,10)
print(r.name)
print(r.price)
print(r.area())
View Code

 


免責聲明!

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



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