1. 程式人生 > >類與類之間的關係:依賴關係和關聯關係及繼承關係中self是什麼? 類裡面的特殊成員

類與類之間的關係:依賴關係和關聯關係及繼承關係中self是什麼? 類裡面的特殊成員

類與類之間的關係


  ⼤千世界, 萬物之間皆有規則和規律. 我們的類和物件是對⼤千世界中的所有事物進行歸
類. 那事物之間存在著相對應的關係. 類與類之間也同樣如此. 在⾯向物件的世界中. 類與類
中存在以下關係:
  1. 依賴關係
  2. 關聯關係
  3. 組合關係
  4. 聚合關係
  5. 繼承關係
  6. 實現關係
  由於python是⼀門弱型別程式語言. 並且所有的物件之間其實都是多型的關係. 也就是說,
所有的東⻄都可以當做物件來使用. 所以我們在寫程式碼的時候很容易形成以上關係. ⾸先. 我
們先看第一種, 也是這些關係中緊密程度最低的一個, 依賴關係.

# 寫一個植物大戰殭屍
#
1. 植物 # 打殭屍. 殭屍掉血 # 2. 殭屍 # 吃植物. 植物掉血 class Plant: def __init__(self, name, hp, ad): # 200 self.name = name self.hp = hp self.ad = ad def attack(self, js): print("植物攻擊殭屍") js.hp -= self.ad print(f"殭屍掉血{self.ad}, 還剩下{js.hp}") class
JiangShi: def __init__(self, name, hp, ad): # 1000 800 self.name = name self.hp = hp self.ad = ad def attack(self, zw): print("殭屍咬植物") zw.hp -= self.ad print(f"植物掉血{self.ad}, 還剩{zw.hp}") # 植物 wd = Plant("歪脖子豌豆", 10, 20) # 殭屍 js = JiangShi("鐵桶殭屍", 200, 1) wd.attack(js) wd.attack(js) wd.attack(js) wd.attack(js) wd.attack(js) js.attack(wd) js.attack(wd) js.attack(wd) js.attack(wd)
# 攻擊殭屍 # 殭屍掉血20, 還剩下180 # 植物攻擊殭屍 # 殭屍掉血20, 還剩下160 # 植物攻擊殭屍 # 殭屍掉血20, 還剩下140 # 植物攻擊殭屍 # 殭屍掉血20, 還剩下120 # 植物攻擊殭屍 # 殭屍掉血20, 還剩下100 # 殭屍咬植物 # 植物掉血1, 還剩9 # 殭屍咬植物 # 植物掉血1, 還剩8 # 殭屍咬植物 # 植物掉血1, 還剩7 # 殭屍咬植物 # 植物掉血1, 還剩6

我用著你. 但是你不屬於我. 這種關係是最弱的.
比如. 公司和僱員之間. 對於正式員⼯, 肯定要簽訂勞動合同. 還得⼩心伺候著. 但是如果是兼
職. 那無所謂. 需要了你就來. 不需要你就可以拜拜了. 這里的兼職(臨時工) 就屬於依賴關係.
我用你. 但是你不屬於我.

 

首先, 我們設計一個場景. 還是最初的那個例子. 要把大象裝冰箱. 注意. 在這個場景中, 其
實是存在了兩種事物的. ⼀個是大象, ⼤象負責整個事件的掌控者, 還有⼀個是冰箱, 冰箱負
責被大象操縱.
⾸先, 寫出兩個類, ⼀個是大象類, 一個是冰箱類.

class Elphant:
    def __init__(self, name):
        self.name = name
    def open(self):
        '''
        開⻔
        return:
        '''
        pass
    def close(self):
        '''
        關⻔
        :retrrn:
       '''
        pass
class Refrigerator:
    def open_door(self):
        print("冰箱⻔門被打開了了")
    def close_door(self):
        print("冰箱⻔門被關上了了")                

  冰箱的功能非常簡單, 只要會開門, 關⻔就行了. 但是大象就沒那麼簡單了. 想想. 大象開
門和關⻔的時候是不是要先找個冰箱啊. 然後呢? 開啟冰箱門. 是不是開啟剛才找到的那個冰
箱⻔. 然後裝⾃己. 最後呢? 關冰箱門, 注意, 關的是剛才那個冰箱吧. 也就是說. 開門和關門
用的是同⼀個冰箱. 並且. 大象有更換冰箱的權利, 想進那個冰箱就進那個冰箱. 這時, ⼤象類
和冰箱類的關係並沒有那麼的緊密. 因為大象可以指定任何一個冰箱. 接下來. 我們把程式碼完
善⼀下.

此時, 我們說, ⼤象和冰箱之間就是依賴關係. 我用著你. 但是你不屬於我. 這種關係是最弱的.
比如. 公司和僱員之間. 對於正式員工, 肯定要簽訂勞動合同. 還得⼩心伺候著. 但是如果是兼
職. 那無所謂. 需要了你就來. 不需要你就可以拜拜了. 這里的兼職(臨時工) 就屬於依賴關
我用你. 但是你不屬於我.

 

 

 關聯關係  .組合關係,   聚合關係


其實這三個在程式碼上寫法是一樣的. 但是, 從含義上是不一樣的.

  1. 關聯關係. 兩種事物必須是互相關聯的. 但是在某些特殊情況下是可以更改和更換的.

 

  2. 聚合關係. 屬於關聯關係中的一種特例. 側重點是xxx和xxx聚合成xxx. 各⾃有各自的
生命週期. 比如電腦. 電腦里有CPU, 硬碟, 記憶體等等. 電腦掛了. CPU還是好的. 還是
完整的個體

  3. 組合關係. 屬於關聯關係中的一種特例. 寫法上差不多. 組合關係比聚合還要緊密. 比如⼈的⼤腦, ⼼髒, 各個器官. 這些器官組合成⼀個人. 這時. 人如果掛了. 其他的東⻄也跟著掛了

 

 

  首先我們看關聯關係:

這個最簡單. 也是最常用的⼀種關係. 比如. 大家都有男女朋友. 男人

關聯著女朋友. 女人關聯著男朋友. 這種關係可以是互相的, 也可以是單⽅的.

 

class Boy:

    def __init__(self, name,  girlFriend=None):
        # 在初始化的時候可以給一個物件的屬性設定成另一個類的物件
        self.girlFriend = girlFriend  # 一個男孩有一個女朋友

    def chi(self):
        if self.girlFriend:
            print(f"帶著他的女朋友{self.girlFriend.name}去吃飯")
        else:
            print("單身狗, 吃什麼吃? 滾去學習.")

    def movie(self):
        if self.girlFriend:
            print(f"帶著他的女朋友{self.girlFriend.name}去看電影")
        else:
            print("單身狗, 看什麼看? 滾去學習.")
class Girl:
    def __init__(self, name):
        self.name = name

b = Boy("寶浪")
g = Girl("孫藝珍")
b.chi()

# alex給包浪介紹了一個女朋. 孫藝珍
b.girlFriend = g
b.chi()

g2 = Girl("梁詠琪")
b.girlFriend = g2 # 換了個女朋友
b.chi()
# 單身狗, 吃什麼吃? 滾去學習.
# 帶著他的女朋友孫藝珍去吃飯
# 帶著他的女朋友梁詠琪去吃飯

  注意. 此時Boy和Girl兩個類之間就是關聯關係. 兩個類的物件緊密練習著. 其中一個沒有
了. 另一個就孤單的不得了. 關聯關係, 其實就是 我需要你. 你也屬於我. 這就是關聯關係. 像
這樣的關係有很多很多. 比如. 學校和老師之間的關係.
School --- 學校
Teacher--- 老師
老師必然屬於⼀個學校. 換句話說. 每個老師肯定有⼀個指定的工作機構. 就是學校. 那老師
的屬性中必然關聯著學校.

 

class School:

    def __init__(self, name):
        self.teach_list = [] # 這裡要裝多個老師
        self.name = name

    def zhaopin(self, teach):
        self.teach_list.append(teach)

    def shangke(self):
        for t in self.teach_list:
            t.work()

class Teacher:
    def __init__(self, name):
        self.name = name
    def work(self):
        print(f"{self.name}在上課")

lnh = School("老男孩")
t1 = Teacher("武sir")
t2 = Teacher("太白")
t3 = Teacher("哪吒")
t4 = Teacher("女神")
t5 = Teacher("日天")
t6 = Teacher("寶浪")

lnh.zhaopin(t1)
lnh.zhaopin(t2)
lnh.zhaopin(t3)
lnh.zhaopin(t4)
lnh.zhaopin(t5)
lnh.zhaopin(t6)

lnh.shangke()
# 武sir在上課
# 太白在上課
# 哪吒在上課
# 女神在上課
# 日天在上課
# 寶浪在上課

  好了. 這就是關聯關係. 當我們在邏輯上出現了. 我需要你. 你還得屬於我. 這種邏輯 就是關
聯關係. 那注意. 這種關係的緊密程度比上面的依賴關係要緊密的多. 為什麼呢? 想想吧
至於組合關係和聚合關係. 其實程式碼上的差別不⼤大. 都是把另一個類的物件作為這個類的
屬性來傳遞和儲存. 只是在含義上會有些許的不同而已.

 

繼承關係


  在⾯向物件的世界中存在著繼承關係. 我們現實中也存在著這樣的關係. 我們說過. x是一
種y, 那x就可以繼承y. 這時理解層面上的. 如果上升到程式碼層面. 我們可以這樣認為. 子類在不
影響父類的程式運行的基礎上對父類進行的擴充和擴充套件. 這里.我們可以把父類被稱為超類或
者基類. 子類被稱為派生類
首先, 類名和物件預設是可以作為字典的key的

 

 

class Foo:
    pass


print(hash(Foo)) # 可雜湊
##-9223371886657599443

print(hash(Foo()))
##150199209592
# 我們寫好的類和建立的物件預設都是可雜湊的


# 去掉可雜湊
class Foo:
    __hash__ = None # 當前類的物件不可雜湊


print(hash(Foo)) # 可雜湊
# -9223371886657599384

# print(hash(Foo())) # TypeError: unhashable type: 'Foo'不可雜湊


class Foo:
    def chi(self, food):
        print("我愛吃魚和", food)

class Bar:
    def chi(self, food):
        print("我愛吃肉和", food)

dic = {Foo: "雞蛋", Bar: "香腸"}

for k, v in dic.items():
    k().chi(v)
# 我愛吃魚和 雞蛋
# 我愛吃肉和 香腸
類名和物件預設是可以作為字典的key的

 

 

 

  接下來. 我們來繼續研究繼承上的相關內容. 在本節中主要研究一下self. 記住. 不管方法之
間如何進行呼叫. 類與類之間是何關係. 預設的self都是訪問這個⽅法的物件.

 

class Base:
    def __init__(self, num):
        self.num = num
    def func1(self):
        print(self.num)
class Foo(Base):
    pass
obj = Foo(123)
obj.func1()
#123

 

class Base:
    def __init__(self, num):
        self.num = num
    def func1(self):
        print(self.num)

class Foo(Base):
    def func1(self):
        print("Foo. func1", self.num)

obj = Foo(123)
obj.func1()
# # Foo. func1 123
# 

 

 

class Base:
    def __init__(self, num):
        self.num = num
    def func1(self):
        print(self.num)
        self.func2()
    def func2(self):
        print("Base.func2")

class Foo(Base):
    def func2(self):
        print("Foo.func2")
obj = Foo(123)
obj.func1()
##123

總結. self在訪問方法的順序: 永遠先找自己的. ⾃己的找不到再找⽗類的.

 

class Base:
    def __init__(self, num):
        self.num = num

    def func1(self):
        print(self.num)
        self.func2()

    def func2(self):
        print(111, self.num)

class Foo(Base):
     def func2(self):
        print(222, self.num)

lst = [Base(1), Base(2), Foo(3)]
for obj in lst:
 obj.func2()
##111 1
##111 2
##222 3

 

 

 

class Base:
    def __init__(self, num):
        self.num = num
    def func1(self):
        print(self.num)
        self.func2()
    def func2(self):
        print(111, self.num)

class Foo(Base):
    def func2(self):
        print(222, self.num)

lst = [Base(1), Base(2), Foo(3)]
for obj in lst:
    obj.func1()

# 1
# 111 1
# 2
# 111 2
# 3
# 222 3

結論: self就是你訪問方法的那個物件. 先找⾃己, 然後在找父類的.

 類中的關係: 依賴關係是最輕的. 最重的是繼承關係. 關聯關係是比較微妙的.

self到底是誰?
self:誰呼叫的就是誰. 型別是根據呼叫方的物件來進行變換的
super:表示的是父類

 

類中的特殊成員


什麼是特殊成員呢? __init_()就是一個特殊的成員. 說白了. 帶雙下劃線的那一坨. 這些方
法在特殊的場景的時候會被自動的執行. 比如,
1. 類名() 會自動執行__init__()
2. 物件() 會⾃動執行__call__()
3. 物件[key] 會⾃動執行__getitem__()
4. 物件[key] = value 會自動執行__setitem__()
5. del 物件[key] 會⾃動執行 __delitem__()
6. 物件+物件 會⾃動執行 __add__()
7. with 物件 as 變量 會自動執行__enter__ 和__exit__
8. 列印物件的時候 會自動執行 __str__
9. 幹掉可雜湊 __hash__ == None 物件就不可雜湊了了.

 ...

......

.........

 

特殊成員:
__init__() # 建立物件的時候初始化操作
__call__() # 物件()
__getitem__() # 物件[哈哈]
__setitem__() # 物件[哈哈] = 值
__new__() # 建立物件的時候.開闢記憶體
__enter__() # with 物件
__exit__() #結束with的時候
__hash__() # 可雜湊 hash()

 

 

建立物件的真正步驟:
  首先, 在執行類名()的時候. 系統會自動先執行__new__()來開闢記憶體. 此時新開闢出來的內
存區域是空的. 緊隨其後, 系統⾃動調⽤__init__()來完成物件的初始化工作. 按照時間軸來算.
  1. 載入類
  2. 開闢記憶體(__new__)
  3. 初始化(__init__)
  4. 使⽤用物件⼲幹xxxxxxxxx
類似的操作還有很多很多. 我們不需要完全刻意的去把所有的特殊成員全都記住. 實戰中也
用不到那麼多. 用到了查就是了.

 

面向物件程式設計的執行流程 ->
  1. 載入類 -> 給類建立一個名稱空間 -> 主要存放類變數.
  2. 建立物件 -> 先找類. -> 根據類來開闢記憶體 ->

  執行類中的__new__() -> 執行__init__() -> 返回物件

 

 

class Foo:
    def __init__(self): # 初始化操作
        print("我是init,  我是老二")
        print("初始化操作. 在建立物件的時候自動呼叫這個方法")

    def __new__(cls, *args, **kwargs): # 建立, 它是真正的構造方法,  可以開闢記憶體
        print("我是new. 我是老大")
        return object.__new__(cls)


    # 為了 物件()
    def __call__(self, *args, **kwargs):
        print("我是物件()")

    # 物件[]
    def __getitem__(self, item):
        print("item=",item)
        print("你執行了__getitem__")
        return "哈哈"

    # 物件[key] = value
    def __setitem__(self, key, value):
        print("key, ", key)
        print("value, ", value)

    # del lst[1]
    def __delitem__(self, key):
        print("key=", key)

    # with 物件:
    def __enter__(self):
        print("我是enter")

    # with 物件: 程式碼執行完畢. 最後執行這裡
    def __exit__(self, exc_type, exc_val, exc_tb):
        print("我叫exit")

    def __len__(self):
        print("我的天哪")
        return 3


f = Foo()    # 自動執行__init__()
f()     # 呼叫-> __call__()
print(callable(f)) # 物件()

print(f["李嘉誠"]) # 自動呼叫__getitem__()
f['jay'] = "林俊杰"

del f['哈哈']

with f:
    print("我是哈哈哈哈")

with open() :


lst = ["孫藝珍", "李金珠", "井柏然"]

lst[2] # =>自動的呼叫__getitem__()


def func():
    pass
func = 3
print(callable(func)) # 判斷xxx是否是可呼叫的


f.__init__()  # 第一次這麼寫. 以後別這麼寫
lst = [1,2,3,4]
it = iter(lst)

print(it.__next__())

print(next(it)) # __next__()

面試之前翻一番
寫出15個特殊成員, 並給出具體作用