1. 程式人生 > 實用技巧 >python基礎--面向物件基礎(類與物件、物件之間的互動和組合、面向物件的名稱空間、面向物件的三大特性等)

python基礎--面向物件基礎(類與物件、物件之間的互動和組合、面向物件的名稱空間、面向物件的三大特性等)

python基礎--面向物件

(1)面向過程VS面向物件

面向過程的程式設計的核心是過程(流水線式思維),過程即解決問題的步驟,面向過程的設計就好比精心設計好一條流水線,考慮周全什麼時候處理什麼東西。

優點是:極大的降低了寫程式的複雜度,只需要順著要執行的步驟,堆疊程式碼即可。

缺點是:一套流水線或者流程就是用來解決一個問題,程式碼牽一髮而動全身。

應用場景:一旦完成基本很少改變的場景,著名的例子有Linux核心,git,以及Apache HTTP Server等。

面向物件的程式設計的核心是物件(上帝式思維),要理解物件為何物,必須把自己當成上帝,上帝眼裡世間存在的萬物皆為物件,不存在的也可以創造出來。面向物件的程式設計好比如來設計西遊記,如來要解決的問題是把經書傳給東土大唐,如來想了想解決這個問題需要四個人:唐僧,沙和尚,豬八戒,孫悟空,每個人都有各自的特徵和技能(這就是物件的概念,特徵和技能分別對應物件的屬性和方法),然而這並不好玩,於是如來又安排了一群妖魔鬼怪,為了防止師徒四人在取經路上被搞死,又安排了一群神仙保駕護航,這些都是物件。然後取經開始,師徒四人與妖魔鬼怪神仙互相纏鬥著直到最後取得真經。如來根本不會管師徒四人按照什麼流程去取。

面向物件的程式設計的

優點是:解決了程式的擴充套件性。對某一個物件單獨修改,會立刻反映到整個體系中,如對遊戲中一個人物引數的特徵和技能修改都很容易。

缺點:可控性差,無法向面向過程的程式設計流水線式的可以很精準的預測問題的處理流程與結果,面向物件的程式一旦開始就由物件之間的互動解決問題即便是上帝也無法預測最終結果。於是我們經常看到一個遊戲人某一引數的修改極有可能導致陰霸的技能出現,一刀砍死3個人,這個遊戲就失去平衡。

應用場景:需求經常變化的軟體,一般需求的變化都集中在使用者層,網際網路應用,企業內部軟體,遊戲等都是面向物件的程式設計大顯身手的好地方。

在python 中面向物件的程式設計並不是全部。

面向物件程式設計可以使程式的維護和擴充套件變得更簡單,並且可以大大提高程式開發效率 ,另外,基於面向物件的程式可以使它人更加容易理解你的程式碼邏輯,從而使團隊開發變得更從容。

瞭解一些名詞:類、物件、例項、例項化

類:具有相同特徵的一類事物(人、狗、老虎),先來定義一個模子,用來描述一類事物,具體相同的屬性和動作

物件/例項:具體的某一個事物(隔壁阿花、樓下旺財)

例項化:類——>物件的過程

類是一個大範圍,是抽象的,是一個模子,它約束了事物有哪些屬性 但是不能約束具體的值

物件是一個具體的內容,是模子的產物,它遵循了類的約束 同時給屬性賦上了具體的值。

Person是一個類,zhouqian和AndreasZhou都是這個類的物件

(2)類和物件

python中一切皆為物件,型別的本質就是類,所以,不管你信不信,你已經使用了很長時間的類了。例如我們學過的list(列表)、tuple(元組)、dict(字典)、set(集合)等等這些都是類。我們在使用的時候,本質上就是一個例項化物件的過程。

>>> dict #型別dict就是類dict
<class 'dict'>
>>> d=dict(name='eva') #例項化
>>> d.pop('name') #向d發一條訊息,執行d的方法pop
'eva'

在python中,用變量表示特徵,用函式表示技能,因而具有相同特徵和技能的一類事物就是‘類’,物件是則是這一類事物中具體的一個。

我們先寫一個類的例子:

class Person: # Person 類名
    def __init__(self): # 這個函式也是執行的
        # 這個方法必須叫__init__這個名字,不能夠改變的,所有的在一個具體的人物出現之後擁有的屬性都可以寫在這裡
        # 這個函式裡面的程式碼,在呼叫的時候才會執行的。不會在建立類的時候就執行。而類裡面的程式碼是在建立了類就會執行。
        print('-'*20)
        self.name = 'alex'
        self.sex = '不詳'
        self.job = '搓澡工'
        self.level = 0
        self.hp = 250
        self.weapon = '搓澡巾'
        self.ad = 1
        print('*' * 20)
        print(self,self.__dict__) # <__main__.Person object at 0x000002AFD568A588> {'name': 'zhouqian', 'sex': '不詳', 'job': '搓澡工', 'levle': 0, 'hp': 250, 'weapon': '搓澡巾', 'ad': 1}
        print('in person') # 這個程式碼是執行的。


# 例項化的一個過程,這裡的zhouqian和類裡面的self是指同一個一樣的東西。
alex = Person()  # 類名() 會自動呼叫類中的__init__()方法  alex就是物件  alex = Person()的過程是通過類獲取一個物件的過程--例項化
print(zhouqian.__dict__) # {'name': 'zhouqian', 'sex': '不詳', 'job': '搓澡工', 'levle': 0, 'hp': 250, 'weapon': '搓澡巾', 'ad': 1}
print(zhouqian, zhouqian.__dict__) # <__main__.Person object at 0x000002AFD568A588> {'name': 'zhouqian', 'sex': '不詳', 'job': '搓澡工', 'levle': 0, 'hp': 250, 'weapon': '搓澡巾', 'ad': 1}

'''
輸出的結果為:
--------------------
********************
<__main__.Person object at 0x0000029C89F59808> {'name': 'zhouqian', 'sex': '不詳', 'job': '搓澡工', 'level': 0, 'hp': 250, 'weapon': '搓澡巾', 'ad': 1}
in person
{'name': 'zhouqian', 'sex': '不詳', 'job': '搓澡工', 'level': 0, 'hp': 250, 'weapon': '搓澡巾', 'ad': 1}
<__main__.Person object at 0x0000029C89F59808> {'name': 'zhouqian', 'sex': '不詳', 'job': '搓澡工', 'level': 0, 'hp': 250, 'weapon': '搓澡巾', 'ad': 1}
'''

在這裡我們進行一個知識點的總結:

1.類的定義,我們是用到了關鍵字class,就和我們函式定義用到的關鍵字def一樣,定義的型別如下所示。

class 類名(父類名):
    '類的文件字串'
    類體
 
這裡的類體包括方法(函式)和靜態屬性
在實際的過程中,很多人分不清楚什麼是函式,什麼是方法?其實方法和函式本質上沒有區別,主要的區別為:函式:如果是類名呼叫,那麼我們就叫函式。方法:如果是例項化物件呼叫,那麼我們就叫做方法。

2.屬性(變數)有兩種一個是靜態屬性(靜態變數)和例項化屬性(例項化變數)

1)靜態屬性(靜態變數)的引用:

class Person:   #定義一個人類
    role = 'person'  #人的角色屬性都是人
    def walk(self):  #人都可以走路,也就是有一個走路方法
        print("person is walking...")

print(Person.role)  #檢視人的role屬性

2)例項化屬性(例項化變數)的引用:

例項化的過程就是類——>物件的過程,例項化物件和self指向的是同一個記憶體地址的空間,可以理解為是同一個。

原本我們只有一個Person類,在這個過程中,產生了一個egg物件,有自己具體的名字、攻擊力和生命值。

語法:物件名 = 類名(引數)

# 先進行例項化
egg = Person('egon')  #類名()就等於在執行Person.__init__()
#執行完__init__()就會返回一個物件。這個物件類似一個字典,存著屬於這個人本身的一些屬性和方法。
print(egg.name)     #檢視屬性直接 物件名.屬性名

3.__init__方法的含義

類名() 會自動呼叫類中的__init__方法

例項化:類名加括號就是例項化,會自動觸發init函式的執行,可以用它來為每個例項定製自己的特徵。這個__init__方法必須是這個名字,不能夠改變的,將例項化變數(例項化屬性)與self繫結在一起,也就是和物件繫結在一起。

我們再看一個上面的例子升級版--面向物件的語法基礎、練習類的建立和例項化

class Person:  # Person 類名
    country = 'China'
    def __init__(self, name, sex, job, level, hp, weapon, ad):  # 這個函式也是執行的
        # 這個方法必須叫__init__這個名字,不能夠改變的,所有的在一個具體的人物出現之後擁有的屬性
        # 都可以寫在這裡
        # 這個函式裡面的程式碼,在呼叫的時候才會執行的。不會在建立類的時候就執行。
        print('-' * 20)
        self.name = name
        self.sex = sex
        self.job = job
        self.level = level
        self.hp = hp
        self.weapon = weapon
        self.ad = ad
        print('*' * 20)
        print(self,self.__dict__)  # <__main__.Person object at 0x000002AFD568A588> {'name': 'zhouqian', 'sex': '不詳', 'job': '搓澡工', 'levle': 0, 'hp': 250, 'weapon': '搓澡巾', 'ad': 1}

# zhouqian和self是一個東西,指向的是一個東西
zhouqian = Person('zhouqian', '不詳', '搓澡工', 0, 250, '搓澡巾', 1)
print(zhouqian)
print(zhouqian.__dict__)
'''
輸出的結果如下:
--------------------
********************
<__main__.Person object at 0x0000018B63CFD208> {'name': 'zhouqian', 'sex': '不詳', 'job': '搓澡工', 'level': 0, 'hp': 250, 'weapon': '搓澡巾', 'ad': 1}
<__main__.Person object at 0x0000018B63CFD208>
{'name': 'zhouqian', 'sex': '不詳', 'job': '搓澡工', 'level': 0, 'hp': 250, 'weapon': '搓澡巾', 'ad': 1}
'''
AndreasZhou = Person('AndreasZhou', 'male', '法師', 100, 500, '打狗棍', 1000)
print(AndreasZhou, AndreasZhou.__dict__)
print(zhouqian, zhouqian.__dict__)
'''
輸出的結果為:
--------------------
********************
<__main__.Person object at 0x000001AC3AC58688> {'name': 'AndreasZhou', 'sex': 'male', 'job': '法師', 'level': 100, 'hp': 500, 'weapon': '打狗棍', 'ad': 1000}
<__main__.Person object at 0x000001AC3AC58688> {'name': 'AndreasZhou', 'sex': 'male', 'job': '法師', 'level': 100, 'hp': 500, 'weapon': '打狗棍', 'ad': 1000}
<__main__.Person object at 0x000001AC3AC44F08> {'name': 'zhouqian', 'sex': '不詳', 'job': '搓澡工', 'level': 0, 'hp': 250, 'weapon': '搓澡巾', 'ad': 1}
'''

# Person是一個類,zhouqian和AndreasZhou都是這個類的物件
# 類有一個空間,儲存的是定義在class中的所有名字
# 每一個物件又擁有自己的空間,通過物件名.__dict__就可以檢視到這個物件的屬性和值
print(zhouqian, zhouqian.__dict__['name'])  # <__main__.Person object at 0x0000022D7F7E4F88> alex
# 通過上面這個方法,我們可以獲取到字典中的鍵值所對應的值。
# 但是上面的獲取步驟有點麻煩,我們的面向物件給了我們一種方法,更加簡單的獲取到字典的鍵值所對應的值。
print(zhouqian, zhouqian.name)  # <__main__.Person object at 0x000001FFBC9F4F88> alex
# 直接通過上面的方法取到值,這個值是表示屬性的值檢視。通過例項化物件zhouqian.屬性name,就可以獲取到相應的值。

# 同時,我們也可以進行屬性值的修改。
zhouqian.name = 'zhouqiansb'  # 屬性的修改
print(zhouqian.name)  # alexsb

# 同時,我們也可以增加屬性
zhouqian.money = 100  # 屬性的增加
print(zhouqian.__dict__, zhouqian.money)
# {'name': 'zhouqiansb', 'sex': '不詳', 'job': '搓澡工', 'level': 0, 'hp': 250, 'weapon': '搓澡巾', 'ad': 1, 'money': 100} 100

# 屬性的刪除
del zhouqian.money  # 屬性的刪除
print(zhouqian.__dict__)
# {'name': 'zhouqiansb', 'sex': '不詳', 'job': '搓澡工', 'level': 0, 'hp': 250, 'weapon': '搓澡巾', 'ad': 1}

print(Person.country) # China
Person.country = 'dasd'
print(Person.country) # dasd
del Person.country
print(Person.country) # type object 'Person' has no attribute 'country'
Person.name = 'zq'
print(Person.name) # zq
print(Person.__dict__) # {'__module__': '__main__', '__init__': <function Person.__init__ at 0x0000020BDE085EE8>, '__dict__': <attribute '__dict__' of 'Person' objects>, '__weakref__': <attribute '__weakref__' of 'Person' objects>, '__doc__': None, 'name': 'zq'}

我們這裡再來一個類的例子,這個例子說的是在物件中定義方法和呼叫方法

class Person():  # Person 類名
    def __init__(self, name, sex, job, level, hp, weapon, ad):
        self.name = name  # 物件屬性/例項變數
        self.sex = sex
        self.job = job
        self.level = level
        self.hp = hp
        self.weapon = weapon
        self.ad = ad
        
    def cuo(self):  # 方法,又是一個必須傳的引數----》self物件,定義方法的時候self就會作為引數自動的傳入
        print('wahaha', self)
        # wahaha <__main__.Person object at 0x0000017FF16F95C8>
        # 所以我們就可以獲取到每一個屬性,或者去操作每一個屬性,用我們定義的函式去操作每一個屬性的值
        print('wahaha', self.name)  # wahaha alex
        print('wahaha', self.name, self.hp, self.ad)  # wahaha alex 250 1


alex = Person('alex', '不詳', '搓澡工', 0, 250, '搓澡巾', 1)  # 物件\例項  類名()---》例項化過程
alex.cuo()  
print(alex)  # <__main__.Person object at 0x0000017FF16F95C8>   self的地址和alex的地址是一模一樣的,指向的是同一個事物,在實際的開發的過程中,如果實在不知道具體是什麼邏輯,就直接認為self和例項化物件是一樣的。


class Dog():  # Person 類名
    def __init__(self, name, blood, aggr, kind):
        self.dog_name = name
        self.hp = blood
        self.ad = aggr
        self.kind = kind

    def tiao(self):
        print(self.__dict__)


littleDog = Dog('小白', 5000, 249, '薩摩耶')
littleDog.tiao() # {'dog_name': '小白', 'hp': 5000, 'ad': 249, 'kind': '薩摩耶'}

littleDog1 = Dog('小金', 6000, 266, '金毛')
littleDog1.tiao() # {'dog_name': '小金', 'hp': 6000, 'ad': 266, 'kind': '金毛'}

在這裡我們進行一個知識點的總結:

  1. zhouqian = Person(),,如果例項化物件,我們沒有傳入引數的時候,這個時候,我們會報錯。報錯的資訊如下:init() takes 1 positional argument but 8 were given

  2. 那麼這些實際引數應該怎麼傳入到這個類中呢?

    首先,我們要知道,在例項化的時候,我們在Person()中傳入的引數,會被放到__init__方法中。

    所以我們要接受這些引數,我們要在__init__方法中接受這些引數。將實際引數的值和物件(self)繫結在一起。

  3. 靜態變數(靜態屬性)和例項化變數(例項化屬性)的值可以進行修改、增加、刪除、增加

  4. 類中方法的呼叫:類.函式名() 物件。方法名()

  5. 類屬性的補充

    一:我們定義的類的屬性到底存到哪裡了?有兩種方式檢視
    dir(類名):查出的是一個名字列表
    類名.__dict__:查出的是一個字典,key為屬性名,value為屬性值
    
    二:特殊的類屬性
    類名.__name__# 類的名字(字串)
    類名.__doc__# 類的文件字串
    類名.__base__# 類的第一個父類(在講繼承時會講)
    類名.__bases__# 類所有父類構成的元組(在講繼承時會講)
    類名.__dict__# 類的字典屬性
    類名.__module__# 類定義所在的模組  __main__
    類名.__class__# 例項對應的類(僅新式類中) # <class 'type'>
    
  6. python是一切皆物件,修改列表、字典、中的某個值,或者是物件的某一個屬性,都不會影響這個物件、字典、列表所在的記憶體空間。

  7. 例項化所經歷的步驟

    1.類名() 之後的第一件事兒:開闢一塊兒記憶體空間

    2.呼叫__init__方法,把空間的記憶體地址作為self引數傳遞到函式內部

    3.所有的這一個物件需要使用的屬性都需要和self關係起來

    4.執行完init中的邏輯之後,self變數會自動的被返回到呼叫處(發生例項化的地方)----self和例項化物件指向的是同一個

  8. 定義和呼叫的固有模式

    class 類名:
        def __init__(self,引數1,引數2):
            self.物件的屬性1 = 引數1
            self.物件的屬性2 = 引數2
    
        def 方法名(self):pass
    
        def 方法名2(self):pass
    
    物件名 = 類名(1,2)  #物件就是例項,代表一個具體的東西
                      #類名() : 類名+括號就是例項化一個類,相當於呼叫了__init__方法
                      #括號裡傳引數,引數不需要傳self,其他與init中的形參一一對應
                      #結果返回一個物件
    物件名.物件的屬性1   #檢視物件的屬性,直接用 物件名.屬性名 即可
    物件名.方法名()     #呼叫類中的方法,直接用 物件名.方法名() 即可
    類名.靜態屬性(靜態變數)
    

(3)物件之間的互動(注重在行為)

我們這裡來一個類的例子,實現物件之間的互動。

class Person():  # Person 類名
    def __init__(self, name, sex, job, level, hp, weapon, ad):
        self.name = name  # 物件屬性/例項變數
        self.sex = sex
        self.job = job
        self.level = level
        self.hp = hp
        self.weapon = weapon
        self.ad = ad

	def cuo(self, dog):
		dog.hp -= self.ad
		print('%s 攻擊了 %s,%s掉了%s點血' % (self.name, dog.dog_name, dog.dog_name, self.ad))

class Dog():  # Dog 類名
    def __init__(self, name, blood, aggr, kind):
        self.dog_name = name
        self.hp = blood
        self.ad = aggr
        self.kind = kind

	 def tiao(self, person):
		person.hp -= self.ad
		print('%s 攻擊了 %s,%s掉了%s點血' % (self.dog_name, person.name, person.name, self.ad))

zhouqian = Person('zhouqian', '不詳', '搓澡工', 0, 250, '搓澡巾', 1)  # 物件\例項  類名()---》例項化過程
littleDog = Dog('小白', 5000, 249, '薩摩耶')
littleDog1 = Dog('小金', 6000, 266, '金毛')

zhouqian.cuo(littleDog)
zhouqian.cuo(littleDog1)
littleDog.tiao(zhouqian)
littleDog1.tiao(zhouqian)
'''
輸出的結果為:
zhouqian 攻擊了 小白,小白掉了1點血
zhouqian 攻擊了 小金,小金掉了1點血
小白 攻擊了 zhouqian,zhouqian掉了249點血
小金 攻擊了 zhouqian,zhouqian掉了266點血
'''

接下來我們用物件的互動實現一個簡單的小遊戲

定義一個人類

class Person:  # 定義一個人類
    role = 'person'  # 人的角色屬性都是人

    def __init__(self, name, aggressivity, life_value, money):
        self.name = name  # 每一個角色都有自己的暱稱;
        self.aggressivity = aggressivity  # 每一個角色都有自己的攻擊力;
        self.life_value = life_value  # 每一個角色都有自己的生命值;
        self.money = money

    def attack(self,dog):
        # 人可以攻擊狗,這裡的狗也是一個物件。
        # 人攻擊狗,那麼狗的生命值就會根據人的攻擊力而下降        dog.life_value -= self.aggressivity

定義一個狗類

class Dog:  # 定義一個狗類
    role = 'dog'  # 狗的角色屬性都是狗

    def __init__(self, name, breed, aggressivity, life_value):
        self.name = name  # 每一隻狗都有自己的暱稱;
        self.breed = breed  # 每一隻狗都有自己的品種;
        self.aggressivity = aggressivity  # 每一隻狗都有自己的攻擊力;
        self.life_value = life_value  # 每一隻狗都有自己的生命值;

    def bite(self,people):
        # 狗可以咬人,這裡的狗也是一個物件。
        # 狗咬人,那麼人的生命值就會根據狗的攻擊力而下降
        people.life_value -= self.aggressivity

接下來,又建立一個新的兵器類。

class Weapon:
    def __init__(self,name, price, aggrev, life_value):
        self.name = name
        self.price = price
        self.aggrev = aggrev
        self.life_value = life_value

    def update(self, obj):  #obj就是要帶這個裝備的人
        obj.money -= self.price  # 用這個武器的人花錢買所以對應的錢要減少
        obj.aggressivity += self.aggrev  # 帶上這個裝備可以讓人增加攻擊
        obj.life_value += self.life_value  # 帶上這個裝備可以讓人增加生命值

    def prick(self, obj):  # 這是該裝備的主動技能,扎死對方
        obj.life_value -= 500  # 假設攻擊力是500

測試互動

lance = Weapon('長矛',200,6,100)
egg = Person('egon',10,1000,600)  #創造了一個實實在在的人egg
ha2 = Dog('二愣子','哈士奇',10,1000)  #創造了一隻實實在在的狗ha2

#egg獨自力戰"二愣子"深感吃力,決定窮畢生積蓄買一把武器
if egg.money > lance.price: #如果egg的錢比裝備的價格多,可以買一把長矛
    lance.update(egg) #egg花錢買了一個長矛防身,且自身屬性得到了提高
    egg.weapon = lance #egg裝備上了長矛,帶上裝備

print(egg.money,egg.life_value,egg.aggressivity)

print(ha2.life_value)
egg.attack(ha2)   #egg打了ha2一下
print(ha2.life_value)
egg.weapon.prick(ha2) #發動武器技能
print(ha2.life_value) #ha2不敵狡猾的人類用武器取勝,血槽空了一半

按照這種思路一點一點的設計類和物件,最終你完全可以實現一個對戰類遊戲。

(4)面向物件的名稱空間(重點)

我們先看一個例子:

class A:
    Country = '中國'  # 靜態變數/靜態屬性
    # 儲存在類的名稱空間裡面的,可以通過類名.變數取到  也可以例項化物件之後,通過例項化物件.變數取到
    def __init__(self):
        # 繫結方法,儲存在類的名稱空間裡面的,可以通過類名.函式名呼叫
        # 但是在實際的開發的過程中,我們都是不會用類去呼叫方法,而是通過例項化物件去呼叫類裡面的方法。
        pass
    def fun1(self):
        print(self) # <__main__.A object at 0x000001B320899B88>
    def fun2(self):
        pass
    def fun3(self):
        pass
    def fun4(self):
        pass
    def fun5(self):
        pass
    Country = '美國'
    print(Country) # 美國
print(A.__dict__)
# {'__module__': '__main__', 'Country': '中國', '__init__': <function A.__init__ at 0x0000015BA6EBB828>,
# 'fun1': <function A.fun1 at 0x0000015BA6EBB3A8>, 'fun2': <function A.fun2 at 0x0000015BA6EBB4C8>, 'fun3': <function
# A.fun3 at 0x0000015BA6EBB5E8>, 'fun4': <function A.fun4 at 0x0000015BA6EBB678>, 'fun5': <function A.fun5 at
# 0x0000015BA6EBB708>, '__dict__': <attribute '__dict__' of 'A' objects>, '__weakref__': <attribute '__weakref__' of
# 'A' objects>, '__doc__': None}

# {'__module__': '__main__', 'Country': '美國', '__init__': <function A.__init__ at 0x0000015BA6EBB828>,
# 'fun1': <function A.fun1 at 0x0000015BA6EBB3A8>, 'fun2': <function A.fun2 at 0x0000015BA6EBB4C8>, 'fun3': <function
# A.fun3 at 0x0000015BA6EBB5E8>, 'fun4': <function A.fun4 at 0x0000015BA6EBB678>, 'fun5': <function A.fun5 at
# 0x0000015BA6EBB708>, '__dict__': <attribute '__dict__' of 'A' objects>, '__weakref__': <attribute '__weakref__' of
# 'A' objects>, '__doc__': None}

# a = A()
# print(a.Country)

print(A.Country) # 美國
# print(A.__init__())  __init__() missing 1 required positional argument: 'self'
print(A.__init__) # <function A.__init__ at 0x000001C52A64B828>
# 類的載入順序是從上到下一次執行的,安裝順序執行的。
a = A()
print(A.Country)
a.fun1()  #==== A.func1(a)

'''
輸出的結果為:
美國
{'__module__': '__main__', 'Country': '美國', '__init__': <function A.__init__ at 0x0000025058C5C828>, 'fun1': <function A.fun1 at 0x0000025058C5C3A8>, 'fun2': <function A.fun2 at 0x0000025058C5C4C8>, 'fun3': <function A.fun3 at 0x0000025058C5C5E8>, 'fun4': <function A.fun4 at 0x0000025058C5C678>, 'fun5': <function A.fun5 at 0x0000025058C5C708>, '__dict__': <attribute '__dict__' of 'A' objects>, '__weakref__': <attribute '__weakref__' of 'A' objects>, '__doc__': None}
美國
<function A.__init__ at 0x0000025058C5C828>
美國
<__main__.A object at 0x0000025058C69D48>
'''

這個例子的類的名稱空間如下所示。系統會為類建立一個名稱空間,存瞭如下的屬性和函式。

我們在舉一個難度大的例子,這個例子設計到繼承的知識。

class Animal(object):
    kind = 'cute'
    def __init__(self, name, sex, height, weight):
        self.name = name
        self.sex = sex
        self.height = height
        self.weight = weight
    def func(self):
        print(self.name, self.sex, self.height, self.weight)
class Dog(Animal):
    def __init__(self, name, sex, height, weight, hobby, food):
        Animal.__init__(self, name, sex, height, weight)
        self.hobby = hobby
        self.food = food
    def func1(self):
        print(self.name, self.sex, self.height, self.weight, self.hobby, self.food)
        print(self.kind)
class Cat(Animal):
    def __init__(self, name, sex, height, weight, hobby, food):
        Animal.__init__(self, name, sex, height, weight)
        self.hobby = hobby
        self.food = food
    def func2(self):
        print(self.name, self.sex, self.height, self.weight, self.hobby, self.food)
        self.kind = 'ugly'
        print(Animal.kind)
dog = Dog('小狗', 'male', 188, 125, '喝', '貓糧')
cat = Cat('小貓', 'male', 178, 105, '玩', '貓糧')
dog.func1()
cat.func2()
dog.func()
cat.func()
print(Animal.__dict__)
'''
輸出的結果為:
小狗 male 188 125 喝 貓糧
cute
小貓 male 178 105 玩 貓糧
cute
小狗 male 188 125
小貓 male 178 105
{'__module__': '__main__', 'kind': 'cute', '__init__': <function Animal.__init__ at 0x000001C50873C4C8>, 'func': <function Animal.func at 0x000001C50873C5E8>, '__dict__': <attribute '__dict__' of 'Animal' objects>, '__weakref__': <attribute '__weakref__' of 'Animal' objects>, '__doc__': None}
'''

在這裡用語言總結一下,建立類的時候,會分配相應的類的名稱空間。在例項化物件的時候,也會分配相應例項化物件的名稱空間。我們首先要弄清楚每一個名稱空間裡面都是存了什麼。在例項化物件中呼叫屬性和方法的時候,我們都是先從例項化物件開始找,找到就是我們要的。如果沒有找到就通過類指標繼續向類中找,類中如果找到了就是我們要的。如果沒有被找到,我們繼續想父類找。。。。以此類推。。

在這裡我們對名稱空間的知識做一個總結:

1.類中的變數是靜態變數/靜態屬性,物件中的變數是屬於是例項變數/例項屬性。

2.物件中的變數只屬於物件本身,每個物件有屬於自己的空間來儲存物件的變數,當使用物件名去呼叫某一個屬性的時候會優先在自己的空間中尋找,找不到再去對應的類中尋找。如果自己沒有就會去引用類的,如果類也沒有那麼就會報錯。對於類來說,類中的變數所有的物件都是可以讀取的,並且讀取的是同一份變數

3.類中的靜態變數的用處:如果一個變數是所有的物件共享的值,那麼這個變數應該被定義成靜態變數。所有和靜態變數相關的增刪改查都應該使用類名來處理,而不應該使用物件名直接修改靜態變數。因為會在物件的名稱空間中建立一個變數。這樣的話就失去了靜態變數的意義。

(5)物件的組合(注重在屬性)

在學習組合之前,我們先了解一下組合這個概念。組合其實是:一個類的物件是另外一個類物件的屬性。這個就是我們說的組合。物件變成了另外一個物件的屬性,這個就是組合

在這裡我們舉一個組合的例子:班級類的物件為學生類物件的一個屬性,這個例子的應用就是組合

1.學生資訊類:姓名、性別、年齡、學號、班級、手機號

2.班級資訊類:班級名稱、開班時間、當前講師

問:我們怎麼取到姓名為zhouqian的班級資訊,怎麼取到姓名為zhouqian的班級名稱,怎麼取到姓名為zhouqian的班級開班時間、怎麼取到姓名為zhouqian的班級當前講師?我們實現的程式碼如下所示。

# 學生類
    # 姓名 性別 年齡 學號 班級 手機號
class Student:
    def __init__(self, name, sex, age, number, cls, phone):
        self.name = name
        self.sex = sex
        self.age = age
        self.number = number
        self.cls = cls
        self.phone = phone
# 班級資訊
    # 班級名稱
    # 開班時間
    # 當前講師
class Cls:
    def __init__(self, cname, begin, teacher):
        self.cname = cname
        self.begin = begin
        self.teacher = teacher
py22 = Cls('python全棧22期', '2019-4-26', '小白')
py23 = Cls('python全棧23期', '2019-5-28', '寶元')
student = Student('大壯', 'male', 18, 27, py23, '13912012012')
student1 = Student('雪飛', 'female', 18, 17, py22, '13912012013')
print(student.cls, py23)
# <__main__.Cls object at 0x000002E529888A48> <__main__.Cls object at 0x000002E529888A48>
print(py23.begin)  # 2019-5-28
# 檢視student(大壯)所在的班級cls的開班日期是多少?
print(student.cls.begin) # 2019-5-28
# 檢視student(雪飛)所在的班級cls的開班日期是多少?
print(student1.cls.begin) # 2019-4-26

在這裡我們在舉一個組合的例子:

1.班級資訊類:班級名稱、開班時間、當前講師、包含一個屬性--課程

2.課程資訊類:課程名稱、課程週期、課程價格

建立兩個班級linux57和python22

檢視linux57期的班級所學課程的價格

檢視python22期的班級所學課程的週期

class cls:
    def __init__(self,cname,begin,teacher,course):
        self.cname = cname
        self.begin = begin
        self.teacher = teacher
        self.course = course
class course:
    def __init__(self,csname,cstime,csprice):
        self.csname = csname
        self.cstime = cstime
        self.csprice = csprice

linux = course('linux','一個月',10000)
python = course('python','兩個月',20000)
linux57 = cls('linux57','2019-1-1','zhouqian',linux)
python22 = cls('python','2019-4-26','andreas',python)

# 檢視linux57期的班級所學課程的價格
print(linux57.course.csprice)
# 檢視python22期的班級所學課程的週期
print(python22.course.cstime)
'''
輸出的結果為:
10000
兩個月
'''

在物件的組合結束之後,我們在這裡普及一個坑,這個坑很多人都會犯錯。仔細觀察下面兩個程式。

# 程式一:
class A:
    Country = '中國'
    def __init__(self,name,age,country):
        self.name = name
        self.age = age
    def Country(self):
        return self.Country
a = A('alex',23,'印度')
b = A('wusir',74,'泰國')
print(a.Country)
print(a.Country())
# <bound method A.Country of <__main__.A object at 0x000001965F6FB2C8>>
# <bound method A.Country of <__main__.A object at 0x000001965F6FB2C8>>

# 程式二:
class A:
    Country = '中國'
    def __init__(self,name,age,country):
        self.name = name
        self.age = age
    def Country1(self):
        return self.Country
a = A('alex',23,'印度')
b = A('wusir',74,'泰國')
print(a.Country)
print(a.Country1())
'''
中國
中國
'''

(6)面向物件的三大特性(繼承、多型、封裝)

1)繼承的基礎知識

在講解類的繼承之前,我們先做一個例子來體驗一下繼承的特別之處。

1.貓類:名字、吃、喝、睡、爬樹

2.狗類:名字、吃、喝、睡、看家

我們沒有使用類的繼承的時候,我們寫的程式碼如下所示。

class Cat:
    def __init__(self,name):
        self.name = name
    def eat(self):
        print(f'{self.name} is eating!')
        # print('%s is eating!' %self.name)
        # print('{0} is eating!'.format(self.name))
    def drink(self):
        print(f'{self.name} is drinking')
    def sleep(self):
        print(f'{self.name} is sleeping!')
    def climb_tree(self):
        print(f'{self.name} is climbing tree')

class Dog:
    def __init__(self,name):
        self.name = name
    def eat(self):
        print(f'{self.name} is eating!')
        # print('%s is eating!' %self.name)
        # print('{0} is eating!'.format(self.name))
    def drink(self):
        print(f'{self.name} is drinking')
    def sleep(self):
        print(f'{self.name} is sleeping!')
    def house_keep(self):
        print(f'{self.name} is house keeping')

cat = Cat('布偶')
cat.eat()  # Cat.eat(cat)
cat.drink()
cat.sleep()
cat.climb_tree()
dog = Dog('薩摩耶')
dog.eat()
dog.drink()
dog.sleep()
dog.house_keep()
'''
輸出的結果為:
布偶 is eating!
布偶 is drinking
布偶 is sleeping!
布偶 is climbing tree
薩摩耶 is eating!
薩摩耶 is drinking
薩摩耶 is sleeping!
薩摩耶 is house keeping
'''

上面的程式碼看起來是十分的累贅,有很多重複的程式碼,那麼我們怎麼解決程式碼的重複的問題呢?這裡是我們需要思考的一個問題。需要解決程式碼的重複問題,使得程式碼更加的簡潔。要解決這個問題,我們需要用到繼承的知識才可以解決,所以我們在這裡引入了繼承的知識。

繼承------需要解決程式碼的重複問題。繼承的語法如下所示。

# 單繼承
class A:
    pass
class B(A):
    pass
# 多繼承
class C:
    pass
class D:
    pass
class E(C,D):
    pass

表示的是B類繼承了A類。稱為A是父類,B是子類。A也可以叫父類、基類、超類。B是子類、派生類。

在這裡我們使用繼承的知識來寫上面的程式碼。

# 子類可以使用父類中的:方法和靜態變數(靜態屬性)
# 當子類和父類的方法重名的時候,我們只使用子類的方法,而不會去呼叫父類的方法了。(在java語言中,這個叫做方法的重寫)
# 子類可以使用父類中的方法,但是也可以重寫父類中的方法,同時也可以使用靜態變數
class Animal:
    def __init__(self, name):
        self.name = name

    def eat(self):
        print(f'{self.name} is eating!')
        # print('%s is eating!' %self.name)
        # print('{0} is eating!'.format(self.name))

    def drink(self):
        print(f'{self.name} is drinking')

    def sleep(self):
        print(f'{self.name} is sleeping!')

class Cat(Animal):
    def eat(self):
        print('%s 吃貓糧'%self.name)
    def climb_tree(self):
        print(f'{self.name} is climbing tree')

class Dog(Animal):
    def house_keep(self):
        print(f'{self.name} is house keeping')

cat = Cat('布偶')
cat.eat()  # Cat.eat(cat)  如果子類也有eat方法,那麼輸出的結果為:布偶 吃貓糧
cat.drink()
cat.sleep()
cat.climb_tree()
dog = Dog('薩摩耶')
dog.eat()
dog.drink()
dog.sleep()
dog.house_keep()
# 有繼承的知識可知,我們寫的程式碼變得更加的整潔,沒有重複程式碼,使得我們寫的程式碼不會冗餘。
'''
繼承的輸出後的結果為:
布偶 吃貓糧
布偶 is drinking
布偶 is sleeping!
布偶 is climbing tree
薩摩耶 is eating!
薩摩耶 is drinking
薩摩耶 is sleeping!
薩摩耶 is house keeping
'''
# 先開闢空間,空間裡有一個類指標--》指向類(Cat)的名稱空間
# self指向這一塊空間。呼叫init方法,物件在自己開闢的空間中
# 找init方法沒有找到,那麼就會去類(Cat)的名稱空間中找init方法,但是還是
# 沒有找到,那麼就會去父類(Animal)中找init方法。
# 繼承的話,也是有一個類指標指向父類。這樣就可以找到父類的方法和靜態變數(靜態屬性)

我們進一步的升級自己的程式碼。

# 貓和狗吃的東西我們完全可以通過傳入引數來實現。
# 子類想要呼叫父類方法的同時還想執行自己的方法
# 貓和狗在呼叫eat方法的時候呼叫自己的eat方法,也呼叫父類的Animal的eat方法
# 在子類中呼叫父類的方法:父類名.方法名(self)  這裡要主動傳入self引數
class Animal:
    def __init__(self, name,food):
        self.name = name
        self.food = food
        self.blood = 100
        self.wise = 100
    def eat(self):
        print(f'{self.name} is eating {self.food}!')

    def drink(self):
        print(f'{self.name} is drinking')

    def sleep(self):
        print(f'{self.name} is sleeping!')

class Cat(Animal):
    def eat(self):
        self.blood += 100
        Animal.eat(self) # 這是一個實現子類想要呼叫父類方法的同時還想執行自己的方法的關鍵程式碼
    def climb_tree(self):
        print(f'{self.name} is climbing tree')
        # Animal.drink(self) 這兩個方法都可以 一樣
        # self.drink()

class Dog(Animal):
    def eat(self):
        self.wise += 100
        Animal.eat(self)
    def climb_tree(self):
        print(f'{self.name} is house keeping')

cat = Cat('布偶','貓糧')
dog = Dog('薩摩耶','狗糧')
cat.eat()
dog.eat()
cat.climb_tree()
'''
布偶 is eating 貓糧!
薩摩耶 is eating 狗糧!
布偶 is climbing tree
'''
print(cat.__dict__)
print(dog.__dict__)
'''
輸出的結果為:
布偶 is eating 貓糧!
薩摩耶 is eating 狗糧!
布偶 is climbing tree
{'name': '布偶', 'food': '貓糧', 'blood': 200, 'wise': 100}
{'name': '薩摩耶', 'food': '狗糧', 'blood': 100, 'wise': 200}
'''

我們用繼承的知識來做下面的一個題目:

class Foo:
    def __init__(self):
        self.func()
    def func(self):
        print('in foo')

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

Son() # 輸出的結果為:in son

我們再用繼承的知識再來做下面一個題目:

class Animal:
    def __init__(self,name,food):
        self.name = name
        self.food = food
        self.blood = 100
        self.wise = 100

    def eat(self):
        print(f'{self.name} is eating {self.food}!')

    def drink(self):
        print(f'{self.name} is drinking')

    def sleep(self):
        print(f'{self.name} is sleeping!')

class Cat(Animal):
    def __init__(self, name, food, eye_color):
        Animal.__init__(self, name, food) # 呼叫了父類的初始化,去完成一些通用屬性的初始化
        self.eye_color = eye_color # 派生屬性,在自己的初始化函式來書寫

cat = Cat('小白','貓糧','藍色')
print(cat.__dict__)
# {'name': '小白', 'food': '貓糧', 'blood': 100, 'wise': 100, 'eye_color': '藍色'}

在這裡我們總結一下繼承的所有知識:

1.繼承語法:class 子類名(父類名):pass class 子類名(父類名1,父類名2...):pass

2.子類可以使用父類中的:方法和靜態變數(靜態屬性)

3.當子類和父類的方法重名的時候,我們只使用子類的方法,而不是去呼叫父類的方法了。(在java語言中這個叫做方法的重寫)

4.子類可以使用父類中的方法,但是也可以重寫父類中的方法,同時也可以使用靜態變數。

5.父類和子類的方法的選擇

子類永遠優先呼叫自己的方法,自己沒有就用父類的方法。如果自己有,還想用父類的,直接在子類方法中呼叫父類的方法。呼叫的方法:父類名.方法名(self)、父類名.方法名(物件)

6.父類和子類的屬性的選擇

子類永遠優先呼叫自己的屬性,自己沒有就用父類的屬性。如果自己有,還想用父類的,直接在子類中呼叫父類的屬性。呼叫的方法:父類名.屬性

7.其實子類和父類的屬性和方法等等之間的關係實質上是面向物件的名稱空間等之間的關係,只要掌握了面向物件名稱空間之間的關係,子類與父類的屬性和方法等等關係我們也可以弄的很清楚。

2)單繼承和多繼承

我們現在來學習一下單繼承和多繼承的知識。

單繼承:只要繼承了一個父類就叫做單繼承(一個父類)

class D:
    pass
class C(D):
    pass
class B(C):
    pass
class A(B):
    pass

多繼承:繼承了兩個或者兩個以上的父類就叫做多繼承(多個父類)

有一些語言是不支援多繼承的,例如java語言。python和C++語言的特點是都可以在面向物件中支援多繼承。

class A:
    def func(self):
        print('in A')
class B:
    def func(self):
        print('in B')
# 只是關係誰寫在前面誰寫在後面
class C(A,B):pass

c = C()
c.func() # in A

總結:

單繼承---調子類的:子類自己有的時候。調父類的:子類自己沒有的時候。調子類和父類的:子類和父類都有,在子類中呼叫父類的。

多繼承---一個類有多個父類,在呼叫父類的時候,按照繼承的順序,先繼承的先尋找。

3)類的繼承順序(單繼承和多繼承)

我們講解類的繼承的順序的時候,我們先講解一下新式類和經典類。

首先我們要明白只要繼承object類就是新式類,不繼承object類的都是經典類。python3中所有的類都是繼承object類,所以都是新式類,所以python3中所有類都是新式類。

經典類:在python3中不存在,在python2中不繼承object的類都是經典類,繼承object類的就是新式類。所以經典類在python3中不存在,在python2中不主動繼承object的類。

# 以下兩個是新式類,因為在python3中
# class A:pass
# class A(object):pass

# 在python2中
# class A:pass  # 經典類
# class A(object):pass  # 新式類

在單繼承方面(無論是新式類還是經典類都是一樣的,都是往深度找。遵循深度優先遍歷的演算法)

class A:
    def func(self):
        pass

class B(A):
    def func(self):
        pass

class C(B):
    def func(self):
        pass

class D(C):
    def func(self):
        pass

d = D()
d.func()

上面程式碼尋找某一個方法的順序(func函式):D-B-C-A-object。越往父類走,是深度。

在多繼承方面(經典類的多繼承的尋找順序是符合深度優先遍歷。新式類是廣度優先遍歷,同時也遵循C3演算法)

# class A:
#     def func(self):
#         print('a')
#
#
# class B(A):
#     def func(self):
#         print('b')
#
# class C(A):
#     def func(self):
#         print('c')
#
#
# class D(B,C):
#     def func(self):
#         print('d')
#
# d = D()
# d.func()  # d

# class A:
#     def func(self):
#         print('a')
#
#
# class B(A):
#     def func(self):
#         print('b')
#
# class C(A):
#     def func(self):
#         print('c')
#
#
# class D(B,C):
#     pass
#
# d = D()
# d.func()  # b


# class A:
#     def func(self):
#         print('a')
#
#
# class B(A):
#     pass
#
# class C(A):
#     def func(self):
#         print('c')
#
#
# class D(B,C):
#     pass
#
# d = D()
# d.func()  # c

#
# class A:
#     def func(self):
#         print('a')
#
#
# class B(A):
#     pass
#
# class C(A):
#     pass
#
#
# class D(B,C):
#     pass
#
# d = D()
# d.func()  # a

在走到一個點,下一個點既可以從深度走,也可以從廣度走的時候,總是先走廣度,再走深度,這一個過程就叫做廣度優先遍歷。

經典類---經典類的多繼承的尋找順序是符合深度優先遍歷。在走到一個點,下一個點既可以從深度走,也可以從廣度走的時候,總是先走深度,再走廣度,這一個過程就叫做深度優先遍歷。深度優先類,在找方法的時候,總是先往深了找,一直找到頭,沒有再找下一條線。在經典類中,都是深度優先,總是在一條路走不通之後再換一條路,走過的點不會再走了。

新式類---新式類的多繼承的尋找順序是符合廣度優先遍歷同時也遵循C3演算法。

class A:
    def func(self):
        print('a')

class B(A):
    def func(self):
        print('b')
class C(A):
    def func(self):
        print('c')
class D(B,C):
    def func(self):
        print('d')

d = D()
d.func()
# D->B->C->A
# [<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]
print(D.mro())

以下是我們掌握新式類演算法的推理過程圖:

| |
| |
| |
| |
| |
| |

經典類---深度優先 新式類---廣度優先

深度優先遍歷演算法,先要會自己看,自己能搞出順序來。

廣度優先遍歷演算法遵循C3演算法,要會用mro,會檢視順序。經典類沒有mro,但是新式類有。

4)多型

我們知道python一切皆物件,同時python處處是多型。

# 多型是站在java的角度來講述
# python中的引數是沒有型別的約束的,在執行的過程當中python直譯器會自動的識別這個是什麼型別的,但是java的引數必須指明資料型別。
# def add(a,b):
#     return a+b

# print(add(1,2))
# print(add(1,'adaduqwhdu')) # TypeError: unsupported operand type(s) for +: 'int' and 'str'

# class Payment:pass
#
# # 微信支付的類
# class WeChat(Payment):
#     def __init__(self, name):
#         self.name = name
#
#     def pay(self, money):
#         dic = {'username': self.name, 'money': money}
#         # 想辦法呼叫微信支付的url連線,把dic傳遞過去
#         print('%s通過微信支付%s錢成功' % (self.name, money))
#
#
# # 蘋果支付類
# class Apple(Payment):
#     def __init__(self, name):
#         self.name = name
#
#     def pay(self, money):
#         dic = {'name': self.name, 'number': money}
#         # 想辦法呼叫微信支付的url連線,把dic傳遞過去
#         print('%s通過微信支付%s錢成功' % (self.name, money))

# java實現方式
# def pay(Payment obj,int money):
#     obj.pay(money)
#
# obj = Apple('alex')
# pay(obj,400)
#
# obj = WeChat('alex')
# pay(obj,400)

總結:一種型別表現出來的多種狀態

支付表現出的微信支付和蘋果支付的兩種狀態

在java情況下:一個引數必須指明指定型別

所以如果想讓兩個型別的物件都可以傳,那麼必須讓這兩個類繼承自一個父類,在指定型別的時候使用我們的父類來指定型別

python處處是多型

5)封裝

封裝:就是把屬性和方法裝起來就是封裝。

封裝有兩種:

一種是廣義上的封裝:大家都基本認可的封裝。把屬性和方法裝起來,不能直接呼叫了。只能通過類和物件呼叫。

一種是狹義上的封裝:一些細節上的封裝。把屬性和方法藏起來,外面不能呼叫,只能在內部偷偷呼叫。

class A:
    a = 1
    b = 2
    def fun(self):
        print('in fun')
print(a)

上面是打印不出來a的值的。這樣已經是不可以調了,某種意義上是已經封裝了。需要通過類的名字去呼叫。這個從某種意義上說已經封裝了。我們之前寫的很多的例子實際上都是廣義上的封裝。

現在我們學習一下狹義上的封裝。

狹義上的封裝可以分為三種。

第一種是例項變數(例項屬性)的封裝(私有化)。給一個名字前面加上了雙下劃線的時候,這個名字就變成了一個私有的。所有的私有的內容或者名字都不能在類的外部呼叫,只能在類的內部使用了。但是我們可以設定方法,使得外部可以獲取到私有的例項變數,和改變私有的例項變數的值。

1.不能看不能改

class User:
    def __int__(self, name, passwd):
        self.user = name
        self.__pwd = passwd

zhouqian = User('alex','sbsbsb')
print(zhouqian.pwd)#報錯

2.只能看,但是不能改

class User:
    def __int__(self, name, passwd):
        self.user = name
        self.__pwd = passwd
    def get_pwd(self): # 表示的是使用者不能改只能看  私有+某個get方法實現的
        return self.__pwd  # 通化這種方式外部可以獲取到這個值,類的外部呼叫,獲取到這個值。
zhouqian = User('zhouqian','sbsbsb')

3.可以看,可以改

class User:
    def __int__(self, name, passwd):
        self.user = name
        self.__pwd = passwd  # 我們這裡的pwd就是一個私有的例項屬性(例項變數)私有的例項變數,私有的物件屬性
    def get_pwd(self): # 表示的是使用者不能改只能看  私有+某個get方法實現的
        return self.__pwd  # 通化這種方式外部可以獲取到這個值,類的外部呼叫,獲取到這個值。
    def change_pwd(self,pwd): # 表示使用者必須呼叫我們自定義的修改方式來進行變數的修改 私有+change方法實現
        self.__pwd = pwd
# 給一個名字前面加上了雙下滑先的時候,這個名字就變成了一個私有的

alex = User('alex','sbsbsb')
print(alex.pwd) #在前面加上__,就會影藏掉,就呼叫不到
print(alex.__pwd) #在前面加上__,就會影藏掉,就呼叫不到

第二種是靜態變數(靜態屬性)的封裝(私有化)。給一個名字前面加上了雙下劃線的時候,這個名字就變成了一個私有的。所有的私有的內容或者名字都不能在類的外部呼叫,只能在類的內部使用了。但是我們可以設定方法,使得外部可以獲取到私有的例項變數,和改變私有的例項變數的值。同時也有三種形式:1.不能看不能改,2.只能看,但是不能改,3.可以看,可以改。

class User:
    __country = 'China' # 私有的靜態變數(靜態屬性)
    def func(self):
        print(self.__country) # 在內的內部可以使用,在內的外部不能被呼叫
        print(User.__country) # 在內的內部可以使用,在內的外部不能被呼叫
    def get_contry(self):
        return User.__country
    def change_country(self):
        User.__country = 'hangzhou'
# print(User.country) # AttributeError: type object 'User' has no attribute 'country'
# print(User.__country) # AttributeError: type object 'User' has no attribute 'country'

user = User()
user.func()
User.func(user)
User().func()
print(User().get_contry())
user1 = User()
user1.change_country()
print(user1.get_contry())
'''
輸出的結果為:
China
China
China
China
China
China
China
hangzhou
'''

第三種是方法的封裝(私有化)

import hashlib
class User:
    def __init__(self,name,passwd):
        self.user = name
        self.__pwd = passwd # 私有的例項變數
    def __get_md5(self): # 私有的方法
        md5 = hashlib.md5(self.user.encode('utf-8'))
        md5.update(self.__pwd.encode('utf-8'))
        return md5.hexdigest()
    def getpwd(self):
        return self.__get_md5()

user = User('alex','123456')
# user.get_md5() # 這個方法變成私有的了,所以這裡是呼叫不到的。外部值呼叫不到的,只能在內部呼叫
# user.__get_md5() # AttributeError: 'User' object has no attribute 'get_md5'
pwd = user.getpwd()
print(pwd) # 94e4ccf5e2749b0bfe0428603738c0f9

總結:所有的私有化都是為了讓使用者不在外部呼叫類中的某個名字(屬性或者方法)。如果完成私有化,那麼這個類的封裝度就更高了,封裝度越高,類中的各種屬性和方法的安全性越高,但是實現的過程程式碼越複雜。

在這裡我們要明白一個事情,就是加了雙下劃線的名字(方法或者屬性)為啥不能從類的外部呼叫了?

class User:
    __country = 'china'
    def func(self):
        print(self.__country)# 在類的內部使用的時候,自動的把當前這句話所在的類的名字拼在私有變數前完成變形
print(User.__dict__)
print(User._User__country) # china
{'__module__': '__main__',  'func': <function User.__func at 0x000001C9E16AC708>, 'country': 'china', '__dict__': <attribute '__dict__' of 'User' objects>, '__weakref__': <attribute '__weakref__' of 'User' objects>, '__doc__': None}
{'__module__': '__main__', '_User__func': <function User.__func at 0x000001C9E16AC708>'_User__country': 'china', '__dict__': <attribute '__dict__' of 'User' objects>, '__weakref__': <attribute '__weakref__' of 'User' objects>, '__doc__': None}

User.__aaa = 'bbb' # 在類的外部根本不能定義類的私有的概念

我們還要明白一個問題是,私有的內容能不能被子類使用呢?答案是不能。私有的內容不能被子類使用。下面兩個程式,我們需要注意,同時也有相應的分析程式的過程。

class Foo(object):
    def __init__(self):
        self.func()

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


class Foo(object):
    def __init__(self):
        self.__func()

    def __func(self):
        print('in Foo')
class Son(Foo):
    def __func(self):
        print('in Son')
Son() # in Foo

| |

在其他語言中的資料的級別都有哪些?在python中有哪些呢?

public 共有的 類內和類外都可以用,父類和子類都可以用 python支援 java c++支援

protect 保護的 類內可以用,父類子類都可以用,類外不能用 python不支援 java c++支援

private 私有的 在本類的類的內部可以用,類內可以用,本類能用,父類子類不能用,類外不能用 python支援 java c++支援