python之面向對象


第一章 面向對象初識


面向對象的三大特性是什么? 抽象、繼承、多態。

面向對象第一個優點:*

對相似功能的函數,同一個業務的函數進行歸類,分類,使你的代碼更清晰化,更合理化。

什么是面向對象。

面向對象的程序設計的核心是對象(上帝式思維),要理解對象為何物,必須把自己當成上帝,上帝眼里世間存在的萬物皆為對象,不存在的也可以創造出來。

那什么是類?什么是對象?

類:就是具有相同屬性和功能的一類事物。

對象:就是類的具體表現。對象一定是不一樣的

面向對象的第二個優點:

面向對象,要擁有上帝的視角看問題,類其實就是一個公共模板(廠房),對象就是從具體的模板實例化出來,得對象,得一切

1. 面向對象的結構

class Human:
    """
    類的具體結構
    """
    #第一部分:靜態屬性
    mind = "思想"
    language ="語言"

    #第二部分:動態方法
    def work(self):
        print("工作")
    def eat(self):
        print("吃飯")

class 是關鍵字與def用法相同,定義一個類。
Human是此類的類名,類名使用駝峰(CamelCase)命名風格,首字母大寫,私有類可用一個下划線開頭。
類的結構從大方向來說就分為兩部分:
靜態變量。
動態方法。

1. 從類名的角度研究類

1.類名操作類中的屬性

class Human:
    """
    類的具體結構
    """
    #第一部分:靜態屬性
    mind = "思想"
    language ="語言"

    #第二部分:動態方法
    def work(self):
        print("工作")
    def eat(self):
        print("吃飯")

1.類名查看類中所有的內容

print(Human.__dict__)

2.類名操作類中的靜態屬性

1.增加

Human.body="有頭發"
print(Human.__dict__)

2.刪除

del Human.mind

3.改

Human.mind ="個性"
print(Human.__dict__)

4.查

print(Human.language)
# print(Human.__dict__)

2.類名操作動態方法

除了兩個特殊方法:靜態方法,類方法之外,一般不會通過類名操作一個類中的方法.

Human.work(123)

總結:一般類名

3. 從對象的角度研究類

1.什么是對象

對象是從類中出來的,只要是類名加上(),這就是一個實例化過程,這個就會實例化一個對象。**

實例化一個對象總共發生了三件事:

  1,在內存中開辟了一個對象空間。

  2,自動執行類中的____init____方法,並將這個對象空間(內存地址)傳給了__init__方法的第一個位置參數self。

  3,在____init__ _方法中通過self給對象空間添加屬性。

class Human:
    """
    類的具體結構
    """
    
    mind = "思想"
    language ="語言"

    def __init__(self,name,age):
# self 和 obj 指向的是同一個內存地址同一個空間,下面就是通過self給這個對象空間封裝四個屬性。
        self.n=name
        self.a=age
    def work(self):
        print("工作")
    def eat(self):
        print("吃飯")

obj=Human("zbb",18) #實例化過程
#得到一個返回值,這個返回值就是對象,實例
print(obj.n)
print(obj.a)
print(obj.__dict__)
#zbb
#{'n': 'zbb'}

2.對象操作對象空間屬性

1.對象查看對象的空間的所有屬性

obj = Human('zbb',18)
print(obj.__dict__)

2. 對象操作對象空間的屬性

obj = Human('zbb',18)
增:
obj.sex = 'laddy_boy'
刪:
del obj.a
改:
obj.a = 1000
查:
print(obj.n)
print(obj.__dict__)

3.對象查看類中的屬性

obj = Human('zbb',18)
# print(obj.mind)
obj.mind = '無腦的'
print(obj.mind)
print(Human.mind)

4.對象調用類中的方法

class Human:

    mind = '有思想'
    language = '實用語言'
    def __init__(self,name,sex,age,hobby):
        self.n = name
        self.s = sex
        self.a = age
        self.h = hobby

    def work(self):
        print(self)
        print('人類會工作')

    def tools(self):
        print('人類會使用工具')

obj = Human('barry','男',18,'運動')
obj.work()
obj.tools()

5.self 是什么?

self其實就是類中方法(函數)的第一個位置參數,只不過解釋器會自動將調用這個函數的對象傳給self。所以咱們把類中的方法的第一個參數約定俗成設置成self, 代表這個就是對象。

一個類可以實例化多個對象

obj1= Human('小胖','男',20,'美女')
obj2= Human('相爺','男',18,'肥女')
print(obj1,obj2)
print(obj1.__dict__)
print(obj2.__dict__)

第二章 類空間問題以及類之間的關系

1.何處可以添加對象屬性

class A:
    def __init__(self,name):
        self.name = name

    def func(self,sex):
        self.sex = sex
# 類外面可以:
obj = A('barry')
obj.age = 18
print(obj.__dict__)  # {'name': 'barry', 'age': 18}

# 類內部也可以:
obj = A('barry') # __init__方法可以。
obj.func('男')  # func 方法也可以。

2.何處可以添加類的靜態屬性

class A:
    def __init__(self, name):
        self.name = name

    def func(self, sex):
        self.sex = sex

    def func1(self):
        A.bbb = 'ccc'

# 類的外部可以添加

A.aaa = 'djb'
print(A.__dict__)

# 類的內部也可以添加。

A.func1(111)
print(A.__dict__)

3. 對象如何找到類的屬性

對象查找屬性的順序:先從對象空間找 ------> 類空間找 ------> 父類空間找 ------->.....

類名查找屬性的順序:先從本類空間找 -------> 父類空間找--------> ........

上面的順序都是單向不可逆,類名不可能找到對象的屬性。

4.類與類之間的關系

  1. 依賴關系
  2. 關聯關系
  3. 組合關系
  4. 聚合關系
  5. 實現關系
  6. 繼承關系(類的三大特性之一:繼承。)

1.依賴關系

主從之分

依賴關系:將一個類的對象或者類名傳到另一個類的方法使用。

2.關聯,組合,聚合關系

  1. 關聯關系. 兩種事物必須是互相關聯的. 但是在某些特殊情況下是可以更改和更換的.
  2. 聚合關系. 屬於關聯關系中的⼀種特例. 側重點是xxx和xxx聚合成xxx. 各⾃有各⾃的聲明周期. 比如電腦. 電腦⾥有CPU, 硬盤, 內存等等. 電腦掛了. CPU還是好的. 還是完整的個體
  3. 組合關系. 給對象封裝一個屬性,屬性值是另一個類的對象
class  Boy:

    def __init__(self,name,girl=None):
        self.name = name
        self.girl=girl

    def hava_diner(self):
        if self.girl:
            print(f"{self.name}請{self.girl}吃飯")
        else:
            print("單身狗")


live=Boy("qw")
# live.hava_diner()
live.girl="zxy"
live.hava_diner()

class  Boy:

    def __init__(self,name,girl=None):
        self.name = name
        self.girl=girl

    def hava_diner(self):
        if self.girl:
            print(f"{self.name}請{self.girl}吃飯")
        else:
            print("單身狗")

    def girl_skill(self):
        self.girl.skill()

class Girl:
    def __init__(self,name):
        self.name=name

    def  skill(self):
        print(f"{self.name}能吃")
zbb=Boy("zbb")
# live.hava_diner()
zxy =Girl("zxy")
zbb.girl=zxy
zbb.girl_skill()

class  Game_role:

    def __init__(self,name,ad,hp):
        self.name=name
        self.ad=ad
        self.hp=hp

    def equipment(self,wea):
        self.weapon =wea   #組合關系


class Weapon:
    def  __init__(self,name,ad):
        self.name = name
        self.ad  = ad

    def weapon_attack(self,p1,p2): #依賴關系
        p2.hp-=self.ad
        print(f"{p1.name}利用{self.name}給了{p2.name}一槍,{p2.name}掉了{self.ad}血,還剩{p2.hp}")

gailun =Game_role("蓋倫",10,100)
yasuo =Game_role("劍豪",20,80)
Sword = Weapon("大保健",16)
Musket = Weapon('菊花槍',20)
# 給游戲人物封裝武器屬性
gailun.equipment(Sword)

gailun.weapon.weapon_attack(gailun,yasuo)

第三章 繼承

1.什么是面向對象的繼承

比較官方的說法就是:

繼承(英語:inheritance)是面向對象軟件技術當中的一個概念。如果一個類別A“繼承自”另一個類別B,就把這個A稱為“B的子類別”,而把B稱為“A的父類別”也可以稱“B是A的超類”。繼承可以使得子類別具有父類別的各種屬性和方法,而不需要再次編寫相同的代碼。在令子類別繼承父類別的同時,可以重新定義某些屬性,並重寫某些方法,即覆蓋父類別的原有屬性和方法,使其獲得與父類別不同的功能。另外,為子類別追加新的屬性和方法也是常見的做法。 一般靜態的面向對象編程語言,繼承屬於靜態的,意即在子類別的行為在編譯期就已經決定,無法在執行期擴充

專業說法: 如果B類繼承A類

B類就稱為子類,派生類

A類稱為父類,基類,超類

通俗一點就是 子承父業

# 繼承的用法:
class Aniaml:
    live = "123"
    def __init__(self,name,sex,age):
            self.name = name
            self.age = age
            self.sex = sex

class Cat(Aniaml):
    pass

class Dog(Aniaml):
    pass

優點: 減少重復代碼 .

增加類之間的耦合度(耦合性不宜多,宜精)

2.繼承的分類

在python2x版本中存在兩種類.:
  ⼀個叫經典類. 在python2.2之前. ⼀直使⽤的是經典類. 經典類在基類的根如果什么都不寫.
  ⼀個叫新式類. 在python2.2之后出現了新式類. 新式類的特點是基類的根是object類。
python3x版本中只有一種類:
python3中使⽤的都是新式類. 如果基類誰都不繼承. 那這個類會默認繼承 object

3.單繼承

1.類名,對象執行父類的方法

class Animal:

    live = "有生命"
    def  __init__(self,name,sex,age):
        self.name =name
        self.sex =sex
        self.age =age

    def eat(self):
        print("chi")

class Human(Animal):
    body = "有思想"
#類名執行父類屬性的方法(不常用)
print(Human.live)
Human.eat(1213)
#子類執行父類的方法
obj = Human("zbb","男","23") #相當於實例化
print(obj.live)

2.執行順序

p1 = Person('barry','男',18)
# 實例化對象時必須執行__init__方法,類中沒有,從父類找,父類沒有,從object類中找。
p1.eat()
# 先要執行自己類中的eat方法,自己類沒有才能執行父類中的方法。

3.同時執行子類以及父類方法

第一種方法(不常用)

class Animal:


    def eat(self):
        print("chi")

class Human:

    def  __init__(self,name):
        self.name =name
    def eat(self):
        print(f"{self.name}需要吃飯")
        Animal.eat(self.name)

obj = Human("zbb")
obj.eat()

第二種方法

class Animal:

    def eat(self):
        print("chi")

class Human(Animal):
    def  __init__(self,name):
        self.name =name

    def eat(self):
        print(f"{self.name}需要吃飯")
        super().eat()

obj = Human("zbb")
obj.eat()

super() 重構父類的方法

解決多重繼承問題

不需要明確給出任何基類的名字,它會自動找到所有直接基類,及其對應的方法.用於繼承.

super()的好處就是可以避免直接使用父類的名字.主要用於多重繼承

4.多繼承

在python2x版本中存在兩種類.:
  ⼀個叫經典類. 在python2.2之前. ⼀直使⽤的是經典類. 經典類在基類的根如果什么都不寫.
  ⼀個叫新式類. 在python2.2之后出現了新式類. 新式類的特點是基類的根是object類。
python3x版本中只有一種類:
python3中使⽤的都是新式類. 如果基類誰都不繼承. 那這個類會默認繼承 object

1.經典類的多繼承

經典類: 深度優先.從左至右,深度優先.

不繼承object類

2.新式類的多繼承

繼承object類

class O:
    name = '1'

class D(O):
    pass

class E(O):
    name = '2'
    # pass
class F(O):
    name = '3'

class B(D,E):
    pass

class C(E,F):
    pass

class A(B,C):
    pass

obj = A()
print(obj.name)

# mro(Child(Base1,Base2)) = [ Child ] + merge( mro(Base1), mro(Base2), [ Base1, Base2] )
# mro(A(B,C)) = [A] + merge(mro(B),mro(C),[B,C])

總結:直接print 不需要什么算法
print(A.mro())
mro是繼承付父類方法時的順序表

第四章 封裝

封裝,顧名思義就是將內容封裝到某個地方,以后再去調用被封裝在某處的內容。

所以,在使用面向對象的封裝特性時,需要:

  • 將內容封裝到某處
  • 從某處調用被封裝的內容

第一步:將內容封裝到某處

self 是一個形式參數,當執行 obj1 = Foo( ) 時,self 等於 obj1

第二步:從某處調用被封裝的內容

調用被封裝的內容時,有兩種情況:

  • 通過對象直接調用
  • 通過self間接調用

1、通過對象直接調用被封裝的內容

上圖展示了對象 ojb1在內存中保存的方式,根據保存格式可以如此調用被封裝的內容:對象.屬性名

class Foo:
    def __init__(self,name,age):
        self.name = name
        self.age = age


obj1 = Foo("zbb",18)
print(obj1.name) # 直接調用obj1對象的name屬性
print(obj1.age) # 直接調用obj1對象的age屬性

2、通過self間接調用被封裝的內容

執行類中的方法時,需要通過self間接調用被封裝的內容

class Foo:
    def __init__(self,name,age):
        self.name = name
        self.age = age
    def func(self):
        print(self.name)
        print(self.age)


obj1 = Foo("zbb",18)
obj1.func() ## Python默認會將obj1傳給self參數

綜上所述,對於面向對象的封裝來說,其實就是使用構造方法將內容封裝到 對象 中,然后通過對象直接或者self間接獲取被封裝的內容。

第五章 多態

多態,同一個對象,多種形態。python默認支持多態。

鴨子類型

python中有一句諺語說的好,你看起來像鴨子,那么你就是鴨子。
對於代碼上的解釋其實很簡答:
class A:
    def f1(self):
        print('in A f1')
    
    def f2(self):
        print('in A f2')


class B:
    def f1(self):
        print('in A f1')
    
    def f2(self):
        print('in A f2')
        
obj = A()
obj.f1()
obj.f2()

obj2 = B()
obj2.f1()
obj2.f2()
# A 和 B兩個類完全沒有耦合性,但是在某種意義上他們卻統一了一個標准。
# 對相同的功能設定了相同的名字,這樣方便開發,這兩個方法就可以互成為鴨子類型。

# 這樣的例子比比皆是:str  tuple list 都有 index方法,這就是統一了規范。
# str bytes 等等 這就是互稱為鴨子類型。

鴨子類型

第六章 類的約束

class  Payment:

    def pay(self,money):
        raise  Exception("你的子類需要定義pay方法")
		#pass

class QQpay(Payment):

    def pay(self,money):
        print(f"使用qq支付了{money}")


class Alipay(Payment):
    def  pay(self,money):
        print(f"使用阿里支付了{money}")


class Wechat(Payment):
    def fuqian(self,money):
        print(f"使用微信支付了{money}")


def pay(obj,money):  #統一接口
    obj.pay(money) # 這個函數就是統一支付規則,這個叫做: 歸一化設計。

obj1 = QQpay()
obj2 = Alipay()
obj3 = Wechat()
pay(obj1,100)
pay(obj2,200)

# pay(obj3,200)  # 會直接找到父類 並發出錯誤

from  abc import   ABCMeta,abstractmethod

class  Payment(metaclass=ABCMeta):
    #抽象類 接口類  規范和約束  metaclass指定的是一個元類
    @abstractmethod
    def pay(self,money):
        pass #抽象方法


class QQpay(Payment):

    def pay(self,money):
        print(f"使用qq支付了{money}")


class Alipay(Payment):
    def  pay(self,money):
        print(f"使用阿里支付了{money}")


class Wechat(Payment):
    def fuqian(self,money):
        print(f"使用微信支付了{money}")
    # def pay(self,money):#強制定義  不定義會報錯
    #     pass

def pay(obj,money): #統一接口
    obj.pay(money)

obj3 = Wechat()   #實例化過程 就報錯了

總結: 約束. 其實就是⽗類對⼦類進⾏約束. ⼦類必須要寫xxx⽅法. 在python中約束的⽅式和⽅法有兩種:

1. 使⽤抽象類和抽象⽅法, 由於該⽅案來源是java和c#. 所以使⽤頻率還是很少的

2.使⽤⼈為拋出異常的⽅案. 並且盡量拋出的是NotImplementError. 這樣比較專業, ⽽且錯誤比較明確.(推薦)

第七章 super()深入了解

super是嚴格按照類的繼承順序執行!!!

# super可以下一個類的其他方法
# 嚴格按照類的mro順序執行

class A:
    def f1(self):
        print('in A')

class Foo(A):
    def f1(self):
        super().f1()
        print('in Foo')

class Bar(A):
    def f1(self):
        print('in Bar')

class Info(Foo,Bar):
    def f1(self):
        super(Foo,self).f1()
        #super(Info,self).f1() #()默認是
        print('in Info f1')

obj = Info()
obj.f1()



第八章 帶顏色的print

書寫格式: 開頭部分:\033[顯示方式;前景色;背景色m + 結尾部分:\033[0m

​ 注意:開頭部分的三個參數:顯示方式,前景色,背景色是可選參數,可以只寫其中的某一個;另外由於表示三個參數不同含義的數值都是唯一的沒有重復的,所以三個參數的書寫先后順序沒有固定要求,系統都能識別;但是,建議按照默認的格式規范書寫。

​ 對於結尾部分,其實也可以省略,但是為了書寫規范,建議\033[***開頭,\033[0m結尾。

-------------------------------------------
-------------------------------------------
字體色     |       背景色     |      顏色描述
-------------------------------------------
30        |        40       |       黑色
31        |        41       |       紅色
32        |        42       |       綠色
33        |        43       |       黃色
34        |        44       |       藍色
35        |        45       |       紫紅色
36        |        46       |       青藍色
37        |        47       |       白色
-------------------------------------------
-------------------------------
顯示方式     |      效果
-------------------------------
0           |     終端默認設置
1           |     高亮顯示
4           |     使用下划線
5           |     閃爍
7           |     反白顯示
8           |     不可見
-------------------------------

數值表示的參數含義:

顯示方式: 0(默認值)、1(高亮)、22(非粗體)、4(下划線)、24(非下划線)、 5(閃爍)、25(非閃爍)、7(反顯)、27(非反顯)

前景色: 30(黑色)、31(紅色)、32(綠色)、 33(黃色)、34(藍色)、35(洋 紅)、36(青色)、37(白色)

背景色: 40(黑色)、41(紅色)、42(綠色)、 43(黃色)、44(藍色)、45(洋 紅)、46(青色)、47(白色)

常見開頭格式
\033[0m 默認字體正常顯示,不高亮
\033[32;0m 紅色字體正常顯示
\033[1;32;40m 顯示方式: 高亮 字體前景色:綠色 背景色:黑色
\033[0;31;46m 顯示方式: 正常 字體前景色:紅色 背景色:青色

舉例說明:

1,

print('\033[1;35;0m字體變色,但無背景色 \033[0m')  # 有高亮 或者 print('\033[1;35m字體有色,但無背景色 \033[0m')
print('\033[1;45m 字體不變色,有背景色 \033[0m')  # 有高亮
print('\033[1;35;46m 字體有色,且有背景色 \033[0m')  # 有高亮
print('\033[0;35;46m 字體有色,且有背景色 \033[0m')  # 無高亮

第九章 類的成員

1.細分類的組成成員

class A:

    company_name = 'zbb'  # 靜態變量(靜態字段)
    __iphone = '1353333xxxx'  # 私有靜態變量(私有靜態字段)


    def __init__(self,name,age): #特殊方法

        self.name = name  #對象屬性(普通字段)
        self.__age = age  # 私有對象屬性(私有普通字段)

    def func1(self):  # 普通方法
        pass

    def __func(self): #私有方法
        print(666)


    @classmethod  # 類方法
    def class_func(cls):
        """ 定義類方法,至少有一個cls參數 """
        print('類方法')

    @staticmethod  #靜態方法
    def static_func():
        """ 定義靜態方法 ,無默認參數"""
        print('靜態方法')

    @property  # 屬性
    def prop(self):
        pass

2.類的私有成員

對於每一個類的成員而言都有兩種形式:

  • 公有成員,在任何地方都能訪問
  • 私有成員,只有在類的內部才能方法

私有成員和公有成員的訪問限制不同

1.靜態字段(靜態屬性)

​ 公有靜態字段:類可以訪問;類內部可以訪問;派生類中可以訪問

​ 私有靜態字段:僅類內部可以訪問;

class A:
    name = 'zbb'
    __name = "zmn"
    def func(self):
        print(self.name)
        print(self.__name)

obj = A()

obj.func()  #內部可以訪問

外部不能訪問

class A:
    name = 'zbb'
    __name = "zmn"
    def func(self):
        pass

obj = A()

print(obj.name)
print(obj.__name) #報錯
print(A.__name) #報錯

2.對象屬性

公有普通字段:對象可以訪問;類內部可以訪問;派生類中可以訪問

私有普通字段:僅類內部可以訪問;

class A:

    def __init__(self,name,pwd):

        self.name = name
        self.__pwd = pwd

    def md5(self):
        self.__pwd = self.__pwd + "123"
obj = A('zbb',"123")
print(obj.__pwd)  ###報錯 不能在類外部以及派生類使用

3.方法:

公有方法:對象可以訪問;類內部可以訪問;派生類中可以訪問

私有方法:僅類內部可以訪問;

class  A:
    def func(self):
        self.__func()
        print("1")
    def __func(self):
        print("2")

obj = A()
obj.func()   #內部可以調用
# obj.__func()  報錯
#####print(A._A__func)  #(不允許!!!)

總結:

私有成員來說: 當你遇到重要的數據,功能,(只允許本類使用的一些方法,數據)設置成私有成員.

ps:非要訪問私有成員的話,可以通過 對象._類__屬性名,但是絕對不允許!!!*

為什么可以通過._類__私有成員名訪問呢?因為類在創建時,如果遇到了私有成員(包括私有靜態字段,私有普通字段,私有方法)它會將其保存在內存時自動在前面加上_類名.

3.類的其他方法

實例方法

定義:第一個參數必須是實例對象,該參數名一般約定為“self”,通過它來傳遞實例的屬性和方法(也可以傳類的屬性和方法);

調用:只能由實例對象調用。

1.類方法

定義:使用裝飾器@classmethod。第一個參數必須是當前類對象,該參數名一般約定為“cls”,通過它來傳遞類的屬性和方法(不能傳實例的屬性和方法);

調用:實例對象和類對象都可以調用。

類方法有什么用???

​ 得到類名可以實例化對象.

​ 可以操作類的屬性.

class  A:

    def func(self):
        print("實例方法")

    @classmethod
    def cls_func(cls):
        print(f"cls{cls}") #cls類名
        obj = cls()
        print(obj)  #打印A的內存地址
        print("類方法")

A.cls_func()
#obj = A()
#obj.cls_func()
# 類方法: 一般就是通過類名去調用的方法,並且自動將類名地址傳給cls,
# 但是如果通過對象調用也可以,但是傳的地址還是類名地址.

例題

創建學生類,只要實例化一個對象,寫一個類方法,統計一下具體實例化多少個學生?

class Student:

    count = 0
    def __init__(self,name,id):

        self.name = name
        self.id = id
        Student.addnum()

    @classmethod
    def addnum(cls):
        cls.count = cls.count + 1

    @classmethod
    def getnum(cls):
        return cls.count

obj1 = Student('zbb', 12343243243) #
obj1 = Student('zbb', 12343243243) #
print(Student.getnum())

2.靜態方法

使用裝飾器@staticmethod。

​ 調用:實例對象和類對象都可以調用。

# # 靜態方法是不依賴於對象與類的,其實靜態方法就是函數.
# 保證代碼的規范性,合理的划分.后續維護性高.
#和在外面沒有區別

import time

class TimeTest(object):

    area = '中國'
    def __init__(self, hour, minute, second):
        self.hour = hour
        self.minute = minute
        self.second = second

    def change_time(self):
        print(f'你想調整的時間: {self.hour}時{self.minute}分{self.second}秒')

    @staticmethod
    def showTime():
        return time.strftime("%H:%M:%S", time.localtime())


def showTime():
    return time.strftime("%H:%M:%S", time.localtime())

def time1():
    pass

def time2():
    pass
# t = TimeTest(2, 10, 10)
# # t.change_time()
# print(TimeTest.showTime())

3.屬性

property是一種特殊的屬性,訪問它時會執行一段功能(函數)然后返回值

將動態方法 偽裝 成了一個屬性,雖然在代碼級別上沒有什么提升,但是讓你看起來更合理

例一:BMI指數(bmi是計算而來的,但很明顯它聽起來像是一個屬性而非方法,如果我們將其做成一個屬性,更便於理解)

成人的BMI數值:
過輕:低於18.5
正常:18.5-23.9
過重:24-27
肥胖:28-32
非常肥胖, 高於32
  體質指數(BMI)=體重(kg)÷身高^2(m)
  EX:70kg÷(1.75×1.75)=22.86

class Bmi:

    def __init__(self,name,height,weight):

        self.name = name
        self.height = height
        self.weight = weight

    def bmi(self):
        return self.weight/self.height**2

obj = Bmi('趙嘎', 1.83, 65)
print(obj.bmi())
結果雖然實現了,但是邏輯上感覺不合理.bmi應該是類似於name,age,height,等名詞,
但是你把它當做方法使用了.

class Bmi:

    def __init__(self,name,height,weight):

        self.name = name
        self.height = height
        self.weight = weight

    @property
    def bmi(self):
        return self.weight/self.height**2

obj = Bmi('趙嘎', 1.83, 65)
# print(obj.bmi)
# property 將執行一個函數需要函數名()變換成直接函數名.
# 將動態方法 偽裝 成了一個屬性,雖然在代碼級別上沒有什么提升,但是讓你看起來更合理.


由於新式類中具有三種訪問方式,我們可以根據他們幾個屬性的訪問特點,分別將三個方法定義為對同一個屬性:獲取、修改、刪除

class Foo:
    @property
    def AAA(self):
        print('get的時候運行我啊')

    @AAA.setter
    def AAA(self,value):
        print('set的時候運行我啊')

    @AAA.deleter
    def AAA(self):
        print('delete的時候運行我啊')

#只有在屬性AAA定義property后才能定義AAA.setter,AAA.deleter
f1=Foo()
f1.AAA
f1.AAA='aaa'
del f1.AAA
# get的時候運行我啊
# set的時候運行我啊
# delete的時候運行我啊
class Foo:
    def get_AAA(self):
        print('get的時候運行我啊')

    def set_AAA(self,value):
        print('set的時候運行我啊')

    def delete_AAA(self):
        print('delete的時候運行我啊')
    AAA=property(get_AAA,set_AAA,delete_AAA) #內置property三個參數與get,set,delete一一對應

f1=Foo()
f1.AAA
f1.AAA='aaa'
del f1.AAA

使用場景

class Goods(object):

    def __init__(self):
        # 原價
        self.original_price = 100
        # 折扣
        self.discount = 0.8

    @property
    def price(self):
        # 實際價格 = 原價 * 折扣
        new_price = self.original_price * self.discount
        return new_price

    @price.setter
    def price(self, value):
        self.original_price = value

    @price.deltter
    def price(self, value):
        del self.original_price

obj = Goods()
obj.price         # 獲取商品價格
obj.price = 200   # 修改商品原價
del obj.price     # 刪除商品原價

商品實例

商品示例

第十章 isinstace 與 issubclass

isinstance(a,b):判斷a是否是b類(或者b類的派生類)實例化的對象

class A:
    pass

class B(A):
    pass

obj = B()

 
print(isinstance(obj,B))
print(isinstance(obj,A))

isinstance

isinstace(a,b) 判斷的是 a類是否是b類 子孫類.**

issubclass(a,b): 判斷a類是否是b類(或者b的派生類)的派生類

class A:
    pass

class B(A):
    pass

class C(B):
    pass

print(issubclass(B,A))
print(issubclass(C,A))

issubclass

思考:那么 list str tuple dict等這些類與 Iterble類 的關系是什么?

from collections import Iterable

print(isinstance([1,2,3], list))  # True
print(isinstance([1,2,3], Iterable))  # True
print(issubclass(list,Iterable))  # True

# 由上面的例子可得,這些可迭代的數據類型,list str tuple dict等 都是 Iterable的子類。

第十章 type與object聯系

type元類是獲取該對象從屬於的類,而type類比較特殊,Python原則是:一切皆對象,其實類也可以理解為'對象',而type元類又稱作構建類,

python中大多數內置的類(包括object)以及自己定義的類,都是由type元類創造的。

print(type('abc'))
print(type(True))
print(type(100))
print(type([1, 2, 3]))
print(type({'name': 'zbb'}))
print(type((1,2,3)))
print(type(object))

class A:
    pass

print(isinstance(object,type))
print(isinstance(A, type))

type 與 object 的關系.
print(type(object)) object類是type類的一個實例.
object類是type類的父類.
print(issubclass(type,object))

第十一章 反射

程序可以訪問、檢測和修改它本身狀態或行為的一種能力(自省)。

python面向對象中的反射:

通過字符串的形式操作對象相關的屬性。

python中的一切事物都是對象(都可以使用反射)

四個可以實現自省的函數

下列方法適用於類和對象(一切皆對象,類本身也是一個對象)

對對象的反射

class Foo:
    f="類的靜態變量"
    def __init__(self,name,age):
        self.name = name
        self.age = age

    def  say_hi(self):
        print(f"hello {self.name}")
#實例化對象
obj = Foo("zbb",23)

#檢測是否含有某屬性
print(hasattr(obj,"name"))
print(hasattr(obj,"say_hi"))
# 獲得屬性(可以獲得是字符串)
n = getattr(obj,"name")
# print(obj.name)
print(n)
func=getattr(obj,'say_hi')
# obj.say_hi()
func()

# print(getattr(obj,'aaaaaaaa')) #報錯
print(getattr(obj,'aaaaaaaa','不存在啊')) #報錯內容自己定義
# 設置屬性
setattr(obj,'sb',True)
setattr(obj,'show_name',lambda self:self.name+'nb')
print(obj.__dict__)
print(obj.show_name(obj))
#刪除屬性
delattr(obj,'age')
delattr(obj,'show_name')
# delattr(obj,'show_name111')#不存在,則報錯
print(obj.__dict__)

對類的反射

class A:
    country = "中國"

    def __init__(self,name,age):
        self.name = name
        self.age =age

    def func(self):
        print(self)
        print("in  A")

if  hasattr(A,"country"):
    print(getattr(A,"country"))

if  hasattr(A,"func"):
    obj = A("zbb","18")
    getattr(obj,"func")()


當前模塊的反射

import sys

def s1():
    print('s1')

def s2():
    print('s2')

this_module = sys.modules[__name__] #本模塊這個對象

hasattr(this_module, 's1')
getattr(this_module, 's2')

在當前模塊中一次性執行4個函數

def func1():
    print('in func1')

def func2():
    print('in func2')

def func3():
    print('in func3')

def func4():
    print('in func4')

import sys

func_lst = [f'func{i}' for i in range(1,5)]
    # print(func_lst)
for func in func_lst:
    getattr(sys.modules[__name__],func)()

對其他模塊的反射

#一個模塊中的代碼
def test():
    print('from the test')

# 另一個模塊中的代碼
import module_test as obj

#obj.test()

print(hasattr(obj,'test'))

getattr(obj,'test')()

反射的應用:

了解了反射的四個函數。那么反射到底有什么用呢?它的應用場景是什么呢?

現在讓我們打開瀏覽器,訪問一個網站,你單擊登錄就跳轉到登錄界面,你單擊注冊就跳轉到注冊界面,等等,其實你單擊的其實是一個個的鏈接,每一個鏈接都會有一個函數或者方法來處理

class User:

    user_list = [('login','登錄'),('register','注冊'),('save', '存儲')]

    def login(self):
        print('歡迎來到登錄頁面')

    def register(self):
        print('歡迎來到注冊頁面')

    def save(self):
        print('歡迎來到存儲頁面')


while 1:
    choose = input('請輸入序號: \n1: 登錄\n2: 注冊\n3: 存儲\n').strip()  # 1
    obj = User()
    getattr(obj, obj.user_list[int(choose)-1][0])()  # getattr(obj,'login')

第十二章 函數 vs 方法

1.通過打印函數(方法)名確定(了解)

def func():
    pass

class A:
    def func(selfs):
        pass


print(func)
print(A.func)#通過類名調用的類中的實例方法叫函數
<function func at 0x00000252B19DC2F0>
<function A.func at 0x00000252B1B997B8>
obj= A()
print(obj.func)#通過對象調用的類中的實例方法叫方法.

2 通過types模塊驗證

from types import FunctionType
from types import MethodType

def func():
    pass

class A:
    def func(self):
        pass

obj = A()

print(isinstance(func,FunctionType))  # True
print(isinstance(A.func,FunctionType))  # True
print(isinstance(obj.func,FunctionType))  # False
print(isinstance(obj.func,MethodType))  # True

總結:
python 中一切皆對象, 類在某種意義上也是一個對象,python中自己定義的類,
以及大部分內置類,都是由type元類(構建類)實例化得來的.
python 中一切皆對象, 函數在某種意義上也是一個對象,函數這個對象是從FunctionType這個類實例化出來的.
python 中一切皆對象, 方法在某種意義上也是一個對象,方法這個對象是從MethodType這個類實例化出來的.

3 靜態方法是函數

4 函數與方法的區別

那么,函數和方法除了上述的不同之處,我們還總結了一下幾點區別。

(1)函數的是顯式傳參的。

(2)函數則跟對象無關。

(3)方法中的數據則是隱形傳參.

(4)方法可以操作類內部的數據。

(5)方法跟對象是關聯的。如我們在用strip()方法是,是不是都是要通過str對象調用,比如我們有字符串s,然后s.strip()這樣調用。是的,strip()方法屬於str對象。

我們或許在日常中會口語化稱呼函數和方法時不嚴謹,但是我們心中要知道二者之間的區別。

在其他語言中,如Java中只有方法,C中只有函數,C++么,則取決於是否在類中。

第十三章 雙下方法

定義:雙下方法是特殊方法

雙下方法主要是python源碼程序員使用的,

我們在開發中盡量不要使用雙下方法,但是深入研究雙下方法,更有益於我們閱讀源碼。

調用:不同的雙下方法有不同的觸發方式,就好比盜墓時觸發的機關一樣,不知不覺就觸發了雙下方法,例如:_init

1. len

class  B:
    def __init__(self,name,age):
        self.name = name

    def __len__(self):
        print(self.__dict__)
        return  len(self.__dict__) #必須要加

b = B("zbb",28)

print(len(b)) #len()觸發調用

2.hash

class A:

    pass

obj = A()

print(hash(obj))
print(hash('fdsaf')) # hash()觸發調用

3.str重點

如果一個類中定義了__str__方法,那么在打印 對象 時,默認輸出該方法的返回值。

class A:

    def __init__(self,name,age):
        self.name = name
        self.age =age

    def __str__(self):
        print(666)
        return f'姓名: {self.name} 年齡: {self.age}'

a = A("zbb",18)
print(f'{a.name},{a.age}') #打印對象名觸發__str__方法
print(str(a)) #直接str轉化也可以觸發.

4.repr

如果一個類中定義了__repr__方法,那么在repr(對象) 時,默認輸出該方法的返回值。

print('我叫%s' % ('zbb'))
print('我叫%r' % ('zbb')) #保留括號里的原來的樣子
print(repr('fdsaf'))
# 我叫zbb
# 我叫'zbb'
# 'fdsaf'


class A:

    def __init__(self,name,age):
        self.name = name
        self.age =age

    def __repr__(self):
        print(666)
        return f'姓名: {self.name} 年齡: {self.age}'

a = A('zbb',18)

# print(a)
print(repr(a)) #print(repr(對象名))  觸發   

5.call重點

class Foo:

    def __init__(self):
        pass

    def __call__(self, *args, **kwargs):
        print('__call__')

obj = Foo()
obj() # 對象名()   或者 類名()()  觸發
# Foo()()

6.eq

class A:
    def __init__(self):
        self.a = 1
        self.b = 2

    def __eq__(self,obj):
        # if  self.a == obj.a and self.b == obj.b:
        #     return True
        return True
x = A()
y = A()
print(x == y) # 對象名 == 對象名    會觸發
節省內存

7.del

class A:

    def __del__(self):
        print(666)

obj = A()
del obj
# del 變量名 或者 類 或者 方法名時會執行     
#不需要去程序員去關心,垃圾處理機制幫助處理

8.new 重點

class A:

    def __init__(self):

        self.x = 1
        print('in init function')

    def __new__(cls, *args, **kwargs):
        print('in new function')
        return object.__new__(A)  # object 342534

a = A()
# 1. 先觸發 object的__new__方法,此方法在內存中開辟一個對象空間.
# 2. 執行__init__方法,給對象封裝屬性.

# python中的設計模式: 單例模式

# 一個類只允許實例化一個對象.
class A:

    pass
obj = A()
print(obj)
obj1 = A()
print(obj1)
obj2 = A()
print(obj2)  #地址都不同

手寫單例模式

方便對實例個數的控制並節約系統資源

class A:
    __msg = None  # 用來記錄對象信息

    def __init__(self,name):
        self.name = name

    def __new__(cls, *args, **kwargs):
        if not cls.__msg:  # 當 __msg 為 None 時,證明是第一次創建對象空間
            # cls.__msg = super().__new__(cls)  # 調用父類的__new__方法。 記得參數為 cls(類本身)
            cls.__msg = object.__new__(cls)
        return cls.__msg  # 將對象空間返回




a = A("zbb")
a1 = A("zxy")
print(a.name)
print(a1.name)  # 兩個地址相同



9.item

# __item__系列
# __getitem__  __setitem__  __delitem__  對對象做類似於字典的(增刪改查)觸發__item__系列
# __delattr__ del obj.屬性  就會觸發此方法
class Foo:
    def __init__(self,name):
        self.name=name

    def __getitem__(self, item):
        print(self.__dict__[item])

    def __setitem__(self, key, value):
        self.__dict__[key]=value
    def __delitem__(self, key):
        print('del obj[key]時,我執行')
        self.__dict__.pop(key)
    def __delattr__(self, item):
        print('del obj.key時,我執行')
        self.__dict__.pop(item)

f1=Foo('sb')
f1['age']=18
f1['age1']=19
del f1.age1
del f1['age']
f1['name']='alex'
print(f1.__dict__)

10.enter exit

上下文管理

# 實例化對象的第二種方式: 必須基於 __enter__ 以及 __exit__這個兩個方法.
class A:

    def __init__(self, text):
        self.text = text

    def __enter__(self):  # 開啟上下文管理器對象時觸發此方法
        self.text = self.text + '您來啦'  # 第一步
        print(11111)
        return self  # 必須!!!將實例化的對象返回f1

    def __exit__(self, exc_type, exc_val, exc_tb):  # 執行完上下文管理器對象f1時觸發此方法
        print(333)  # 第三步
        self.text = self.text + ',這就走啦'


with A('大爺') as f1:
    print(2222)
    print(f1.text)  # 第二步
print(f1.text)  # 第四步

11.iter

class A:

    def __init__(self,name):
        self.name = name

    def __iter__(self):
        for i in range(10):
            yield i


obj = A('zbb')  # obj 一個可迭代對象
# print('__iter__' in dir(obj))
for i in obj:
    print(i)

class A:

    def __init__(self,name):
        self.name = name

    def __iter__(self):
        for i in range(10):
            yield i

obj = A('zbb')  # obj 一個可迭代對象

o = iter(obj)
print(next(o))
print(next(o))
print(next(o))


免責聲明!

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



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