面對物件2

高高有點ka發表於2020-11-29

一. 面對物件三大特徵介紹

  • 封裝(隱藏)

通過私有屬性,私有方法的方式進行封裝

  • 繼承

繼承可以讓子類繼承父類特性,提高程式碼重用性
是一種增量進化,在父類基礎上增加功能,改進演算法

  • 多型

同一種方法呼叫物件不同會產生不同行為

1. 繼承

  1. 已有的類稱為父類或基類,新的類稱為子類或派生類
  2. Python支援多重繼承,一個子類可以繼承多個父類
  3. 類定義中若沒有指定父類,則預設是object類,它是所有類的父類,裡面定義了一些類共有的預設操作,比如__new__
  4. 定義子類時,必須在其建構函式內呼叫父類的建構函式
#測試繼承的基本使用
class Person:     #可以不定義構造器

    def say_age(self):
        print("年齡我不知道")

class Student(Person):
    pass

print(Student.mro())   #檢視繼承結構

s=Student()
s.say_age()


# 測試繼承的基本使用
class Person:

    def __init__(self,name,age):
        self.name=name
        self.__age=age    #父類的私有屬性,子類也會繼承,但是要帶上父類名去呼叫

    def say_age(self):
        print("年齡我不知道")


class Student(Person):

    def __init__(self,name,age,score):
        Person.__init__(self,name,age)    #子類必須顯示呼叫父類構造器,共享父類程式碼
        self.score=score    #自己特有的屬性


print(Student.mro())  # 檢視繼承結構

s = Student("gao",18,72)
s.say_age()
print(s.name)
#print(s.age) #'Student' object has no attribute 'age'
print(s.__dict__)   #{'name': 'gao', '_Person__age': 18, 'score': 72}說明繼承了,不過名字裡有父類
print(s._Person__age)

1. 類成員的繼承與重寫

  1. 子類繼承了父類除構造方法之外的所有成員(包括私有屬性和方法,但要通過父類名來呼叫
  2. 方法重寫:子類可以重新定義父類中的方法,這樣會覆蓋父類的方法
class Person:

    def __init__(self, name, age):
        self.name = name
        self.__age = age  # 父類的私有屬性,子類也會繼承,但是要帶上父類名去呼叫

    def say_age(self):
        print("我的年齡是:",self.age)
    def say_name(self):
        print("我的名字是:{0}".format(self.name))


class Student(Person):

    def __init__(self, name, age, score):
        Person.__init__(self, name, age)  # 子類必須顯示呼叫父類構造器,共享父類程式碼
        self.score = score  # 自己特有的屬性
    def say_name(self):
        print("報告老師,我的名字是:",self.name)


s = Student("gao", 18, 72)
s.say_name()

2. 常用方法

3. 多重繼承

  1. 多重繼承使類的整體層次異常複雜,儘量避免使用
  2. 父類出現相同名字方法,從左至右搜尋
  3. mro也是按從左至右的順序
class A:
    def aa(self):
        print("aa")
    def say(self):
        print("AAA")
class B:
    def bb(self):
        print("bb")
    def say(self):
        print("BBB")
class C(B,A):     #沒有建構函式,也可以繼承
    def __init__(self,name):
        self.name=name
    def cc(self):
        print("cc")

c=C(3)
print(C.mro())  #[<class '__main__.C'>, <class '__main__.B'>, <class '__main__.A'>, <class 'object'>]
c.aa()
c.bb()
c.cc()
c.say()   #父類中出現名稱相同的方法,按從左向右搜尋


4. super()獲得父類定義

  1. 子類中想獲得父類方法,可以通過super()
  2. 和直接用父類名呼叫效果一樣
class A:
    def say(self):
        print("A:",self)
        print("say AAA")
        
class B(A):
    def say(self):
        super().say()   #和直接用A.say()效果一樣
        print("B:",self)


b=B()
b.say()

2. 多型

  1. 多型指同一方法呼叫由於物件不同會產生不同行為
  2. 方法有多型,屬性沒有多型
  3. 多型必要條件:繼承,方法重寫
class Man:
    def eat(self):
        print("餓了,吃飯")

class Chinese(Man):
    def eat(self):
        print("用筷子吃飯")

class English(Man):
    def eat(self):
        print("用叉子吃飯")
class Indian(Man):
    def eat(self):
        print("用右手吃飯")
    
def manEat(m):
    if isinstance(m,Man):
        m.eat()
    else:
        print("不能吃飯")
#Chinese.eat()    #TypeError: eat() missing 1 required positional argument: 'self'
manEat(Chinese)
manEat(English())   #有沒有括號都可以

二. 特殊方法和屬性

1. 特殊方法和運算子過載

  1. Python的運算子實際上是通過呼叫物件的特殊方法實現的
  2. 常見的特殊方法:

__init __: 構造方法 (物件建立:p=Person())
__del __:析構方法
__call __:函式呼叫(函式名(引數)直接呼叫被__call __定義過的方法)
__len __:長度

  1. 每個運算子都對應了相應方法:

+:__add __
-:__sub __
<,<=,==,>,>=,!=:__lt __, __le __, __eq __, __gt __, __ge __, __ne __
|,^,&:__or __, __xor __, __and __
<<,>>:__lshift __, __rshift __
*,/,%,//:__mul __, __truediv __, __mod __, __floordiv __
** : __pow __

  1. 可以重寫上述特殊方法,實現運算子過載
class Person:
    def __init__(self,name):
        self.name=name
    def __add__(self,other):
        if isinstance(other,Person):
            return("{0}--{1}".format(self.name,other.name))   #注意是other.name
        else:
            return "不是同類"
    def __mul__(self,other):
        if isinstance(other,int):
            return self.name*other
        else:
            return"不是同類物件"
        

p1=Person("gao")
p2=Person("peng")

x=p1+p2     #加號呼叫的是__add__函式
print(x)

print(p1*3)     #乘號呼叫的是__mul__函式

2. 特殊屬性

class A:
    def aa(self):
        print("aa")
    def say(self):
        print("AAA")
class B:
    def bb(self):
        print("bb")
    def say(self):
        print("BBB")
class C(B,A):     #沒有建構函式,也可以繼承
    def __init__(self,name):
        self.name=name
    def cc(self):
        print("cc")

c=C(3)
c.aa()
c.bb()
c.cc()
c.say()   #父類中出現名稱相同的方法,按從左向右搜尋
print(dir(c))     #dir()是內建函式
print(c.__dict__)
print(c.__class__)
print(C.__bases__)  #只有一個父類用base,必須用C(型別名),而不是c
print(C.mro())    #必須用C(型別名),而不是c
print(A.__subclasses__)  #用型別名

三. 淺拷貝與深拷貝

  1. 變數的賦值:只是形成兩個變數指向同一物件
  2. 淺拷貝:物件包含的子物件內容不拷貝,源物件與拷貝物件引用同一子物件
  3. 遞迴拷貝物件中包含的子物件,源物件與拷貝物件的子物件不同
#測試物件的淺拷貝,深拷貝
import copy     #copy要匯入copy包
class MobilePhone:
    def __init__(self,CPU,screen):
        self.CPU=CPU
        self.screen=screen

class CPU:
    def calculate(self):
        print("算1234567")
        print("cpu物件:",self)

class Screen:
    def show(self):
        print("顯示一個好看的畫面")
        print("screen物件:",self)

#測試變數賦值
c1=CPU()
c2=c1
print(c2)
print(c1)
'''地址相同
<__main__.CPU object at 0x000001870105EC48>
<__main__.CPU object at 0x000001870105EC48>
'''

#測試淺複製
s1=Screen()
m1=MobilePhone(c1,s1)
m2=copy.copy(m1)
print(m1,m1.CPU,m1.screen)
print(m2,m2.CPU,m2.screen)
'''
m1和m2不同,cpu和screen相同
<__main__.MobilePhone object at 0x0000018701066088> <__main__.CPU object at 0x000001870105EC48> <__main__.Screen object at 0x000001870105EB88>
<__main__.MobilePhone object at 0x0000018701066108> <__main__.CPU object at 0x000001870105EC48> <__main__.Screen object at 0x000001870105EB88>
'''
#測試深複製
m3=copy.deepcopy(m1)
print(m1,m1.CPU,m1.screen)
print(m3,m3.CPU,m3.screen)
'''
都不相同
<__main__.MobilePhone object at 0x0000018701066088> <__main__.CPU object at 0x000001870105EC48> <__main__.Screen object at 0x000001870105EB88>
<__main__.MobilePhone object at 0x0000018700FE97C8> <__main__.CPU object at 0x0000018700FEB048> <__main__.Screen object at 0x0000018701066148>
'''

四. 組合

is關係用繼承,has關係用組合

#測試組合
#繼承:
class A1:
    def say_a1(self):
        print("a1,a1,a1")
    
class A2(A1):
    pass

a2=A2()
a2.say_a1()

#組合:
class A1():
    def say_a1(self):
        print("a1,a1,a1")
    
class A2:
    def __init__(self,a):
        self.a=a    

a1=A1()
a2=A2(a1)  #或直接寫成a2=A2(A1())
a2.a.say_a1()

五. 練習

1. 工廠模式

class CarFactory:
    def create_car(self,brand):
        if brand=="賓士":
            return Benz()
        elif brand=="寶馬":
            return BMW()
        elif brand== "比亞迪":
            return BYD()
        else:
            return "未知品牌,無法建立"
        
    
class Benz:
    pass

class BMW:
    pass

class BYD:
    pass

factory=CarFactory()
c1=factory.create_car("賓士")
c2=factory.create_car("寶馬")
print(c1,c2)

2. 單例模式

class MySingleton:
    
    __obj=None  #類屬性
    
    def __new__(cls,*args,**kwargs):
        if cls.__obj==None:
            cls.__obj=object.__new__(cls)     #呼叫object類的__new__方法建立物件
        return cls.__obj
    def __init__(self,name):
        print("init...")
        self.name=name
        
a=MySingleton("aa")
b=MySingleton("bb")
print(a)
print(b)
'''建立了一次,但是初始化兩次'''

class MySingleton:
    
    __obj=None  #類屬性
    __init_flag=True
    
    def __new__(cls,*args,**kwargs):
        if cls.__obj==None:
            cls.__obj=object.__new__(cls)     #呼叫object類的__new__方法建立物件
        return cls.__obj
    def __init__(self,name):
        if MySingleton.__init_flag:
            print("init...")
            self.name=name
            MySingleton.__init_flag=False   #不能寫成self.__init_flag
a=MySingleton("aa")
b=MySingleton("bb")
print(a)
print(b)

相關文章