1. 程式人生 > >python基礎教程(第三版)學習筆記(七)

python基礎教程(第三版)學習筆記(七)

第七章 再談抽象
自定義`類和物件
7.1 物件魔法
多型:可對不同型別的物件執行相同的操作,而這些操作就像“被施了魔法”一樣能夠正常執行。
封裝:對外部隱藏有關物件工作原理的細節。
繼承:可基於通用類創建出專用類。
7.1.1 多型
大致意味著即便你不知道變數指向的是哪種物件,也能夠對其執行操作,且操作的行為將隨物件所屬的型別(類)而異。

7.1.2 多型和方法
與物件屬性相關聯的函式稱為方法。
標準庫模組random包含一個名為choice的函式,它從序列中隨機選擇一個元素。下面使用這個函式給變數提供一個值。
'''

from random import choice 
x = choice(['Hello, world!', [1, 2, 'e', 'e', 4],"張三"]) 
print(x)


'''
執行這些程式碼後,x可能包含字串'Hello, world!',也可能包含列表[1, 2, 'e', 'e', 4]。具體是哪一個,你不知道也不關心。你只關心x包含多少個'e',而不管x是字串還是列表你都能找到答案。者就是方法的多型。

張三


------------------
(program exited with code: 0)

請按任意鍵繼續. . .


7.1.3 封裝
指的是向外部隱藏不必要的細節。這聽起來有點像多型(無需知道物件的內部細節就可使用它)。這兩個概念很像,因為它們都是抽象的原則。它們都像函式一樣,可幫助你處理程式的組成部分,讓你無需關心不必要的細節。
7.1.4 繼承
繼承是類的繼承,以基類為基礎而建立新類或者是專用類。

7.2 類
7.2.1 類是什麼
類是物件模型,物件是建立在類的基礎上的。張三是人類,黑子是犬類,人和犬是動物類。其中張三和黑子是物件,它是人類和犬類的具體化,而人類和犬類是繼承了動物類特性的子類。
7.2.2 建立自定義類
和大部分程式語言一樣建立類用關鍵字class。比如建一個person(人)類,人有姓名,人會說“你好”。
'''

class Person:
    name=""
    def set_name(self,name):
        self.name=name
    def get_name(self):
        return self.name
    def greet(self):
        print("{}你好!".format(self.name))
        


'''
這個類中有一個屬性三個方法,分別是設定姓名、獲取姓名和輸出你好!誰誰;但它只是個模板不是個具體的person(人),還不能直接執行,還必須讓它具體到“具體的人”,才可以在程式中起作用,那就是用這個模板創造個物件。
'''

foo=Person()
bar=Person()        #建立了foo和bar兩個物件。
foo.set_name("張三")
bar.set_name("李四")   #為這兩個物件的name屬性賦值(起名字)
foo.greet()
bar.greet()           #兩個物件個輸出一段話

'''

張三你好!
李四你好!


------------------
(program exited with code: 0)

請按任意鍵繼續. . .


self是什麼。對foo呼叫set_name和greet時,foo都會作為第一個引數自動傳遞給它們。我將這個引數命名為self,這非常貼切。實際上,可以隨便給這個引數命名,但鑑於它總是指向物件本身,因此習慣上將其命名為self。顯然,self很有用,甚至必不可少。如果沒有它,所有的方法都無法訪問物件本身——要操作的屬性所屬的物件。
'''
'''

class Person:
    名=""
    def set_名(這個,名):
        這個.名=名
    def get_名(這個):
        return 這個.名
    def greet(這個):
        print("{}你好!".format(這個.名))
        
foo=Person()
bar=Person()        #建立了foo和bar兩個物件。
foo.set_名("張三")
bar.set_名("李四")   #為這兩個物件的name屬性賦值(起名字)
foo.greet()
bar.greet()  

'''
'''

張三你好!
李四你好!


------------------
(program exited with code: 0)

請按任意鍵繼續. . .


7.2.3 屬性、函式和方法
實際上,方法和函式的區別表現在引數self上。方法(更準確地說是關聯的方法)將其第一個引數關聯到它所屬的例項,因此無需提供這個引數。無疑可以將屬性關聯到一個普通函式,但這樣就沒有特殊的self引數了。
類中的成員分為例項屬性、例項方法、類屬性、類方法、靜態方法等
i、類屬性:直接在類中定義的屬性,可以通過類或例項物件來訪問。
ii、例項方法:將self作為第一個引數的方法。
iii、類方法:使用@classmethod修飾的方法,將cls作為第一個引數。
iv、靜態方法:使用@staticmethod修飾的方法,沒有任何必選引數,不需要將cls作為第一個引數。
v、例項屬性:在例項中賦予的屬性。
例:
'''

class X:                 #定義類X
    x="x"                #類屬性
    def show_x(self):    #例項方法
        pass
    @classmethod
    def run(cls):        #類方法
        pass
    @staticmethod
    def hold(cls):       #靜態方法
        bass
        
case_x=X()               #例項化物件
case_x.name_for_x="Class_X"  #例項屬性



'''
7.2.4 再談隱藏
隱藏又叫封裝。Python沒有為私有屬性提供直接的支援,要讓方法或屬性成為私有的,只能在類的內部訪問。(不能從外部訪問),只需讓其名稱以兩個下劃線打頭即可。
'''
'''

class Y:
    __y_name        #私有屬性
    pass
    
y=Y()
y.__y_name

'''    


'''

    __y_name        #私有屬性
NameError: name '_Y__y_name' is not defined


------------------
(program exited with code: 1)

請按任意鍵繼續. . .


7.2.5 類的名稱空間
在class語句中定義的程式碼都是在一個特殊的名稱空間(類的名稱空間)內執行的,而類的所有成員都可訪問這個名稱空間。類定義其實就是要執行的程式碼段。
7.2.6 指定超類
子類擴充套件了超類的定義。要指定超類,可在class語句中的類名後加上超類名,並將其用圓括號括起。
'''

class Student(Person):
    school=""
    score=""
    def motion(self):
        print("{}打籃球".format(self.name))
    def get_school(self):
        return self.school
    def    set_scholl(self,school):
        self.school=school
    def    get_score(self):
        return score
    def set_score(self,score):
        self.score=score
        
stu=Student()
stu.school="倫敦大學"
stu.set_name("王五")
stu.motion()
print(stu.school)    

'''

王五打籃球


------------------
(program exited with code: 0)

請按任意鍵繼續. . .


這裡的Person就是student的超類,它繼承了Person類的包括name在內的成員。但如果子類重定義父類(這裡是pPerson)成員,它將執行子類的成員而非父類。
7.2.7 深入探討繼承   
要確定一個類是否是另一個類的子類,可使用內建方法issubclass。
'''

print(issubclass(Student,Person))


'''

True


------------------
(program exited with code: 0)

請按任意鍵繼續. . .


如果你有一個類,並想知道它的基類,可訪問其特殊屬性__bases__。
'''

print(Student.__bases__)

'''

(<class '__main__.Person'>,)


------------------
(program exited with code: 0)

請按任意鍵繼續. . .


要確定物件是否是特定類的例項,可使用isinstance。
'''

print(isinstance(stu,Student))
print(isinstance(stu,Person))

'''

True
True


------------------
(program exited with code: 0)

請按任意鍵繼續. . .


stu是Student類的(直接)例項,但它也是Person類的間接例項,因為Student是Person的子類。換而言之,所有Student物件都是Person物件。從前一個示例可知,isinstance也可用於型別,如字串型別(str)。
如果你要獲悉物件屬於哪個類,可使用屬性__class__。
'''

print(stu.__class__)
print(str.__class__)
print("a".__class__)
print(int.__class__)
print((15).__class__)
print((3.14).__class__)

'''

<class '__main__.Student'>
<class 'type'>
<class 'str'>
<class 'type'>
<class 'int'>
<class 'float'>


------------------
(program exited with code: 0)

請按任意鍵繼續. . .


對於例項還可使用type(s)來獲悉其所屬的類。
7.2.8 多個超類
python是允許多重繼承的。也就是一個類可以有多個父類。
'''

class A:
    def show_a(self):
        print("這是類A")
class B:
    def show_b(self):
        print("這是類B")
class C:
    def show_c(self):
        print("這是類C")
class D(A,B,C):
    def show_d(self):
        print("這是類D")
    
print(issubclass(D,A))
print(issubclass(D,B))
print(issubclass(D,C))    
isd = D()
isd.show_a()
isd.show_b()
isd.show_c()
isd.show_d()

'''

True
True
True
這是類A
這是類B
這是類C
這是類D


------------------
(program exited with code: 0)

請按任意鍵繼續. . .


多重繼承是一個功能強大的工具。然而,除非萬不得已,否則應避免使用多重繼承,因為在有些情況下,它可能帶來意外的“併發症”。使用多重繼承時,有一點務必注意:如果多個超類以不同的方式實現了同一個方法(即有多個同名方法),必須在class語句中小心排列這些超類,因為位於前面的類的方法將覆蓋位於後面的類的方法。
7.2.9 介面和內省
介面這一概念與多型相關。處理多型物件時,你只關心其介面(協議)——對外暴露的方法和屬性。
(《python基礎教程(第三版)》這一節有點文不對題——其實這一章寫的也有點亂)
下面按照此處內容介紹幾個有關的知識點吧
1、hasattr
hasattr() 函式用於判斷物件是否包含對應的屬性或方法。
格式是hasattr(object, name),其中object為物件。name為屬性名或方法名字串。

'''

class Coordinate:
    x=10
    y=-5
    z=0
    def sub(self,s):
        pass
   
 
point1 = Coordinate() 
print(hasattr(point1, 'x'))
print(hasattr(point1, 'y'))
print(hasattr(point1, 'z'))
print(hasattr(point1, 'no'))
print(hasattr(point1, 'sub'))

'''

True
True
True
False
True


------------------
(program exited with code: 0)

請按任意鍵繼續. . .


2、callable
用於檢查一個物件是否是可呼叫的。如果返回True,object仍然可能呼叫失敗;但如果返回False,呼叫物件ojbect絕對不會成功。對於函式, 方法, lambda 函式式, 類, 以及實現了 __call__ 方法的類例項, 它都返回 True。 
格式:callable(object)其中object為要檢驗的物件。
'''

class Z:
    is_z=20
    def __call__(self):
        pass
        
z=Z()
def f():
    pass
    
print(callable(isd))
print(callable(A))
print(callable(isd.show_a))
print(callable(5))
print(callable(z))
print(callable(f))

'''

False
True
True
False
True
True


------------------
(program exited with code: 0)

請按任意鍵繼續. . .


3、getattr
用於返回一個物件屬性值。
格式getattr(object, name[, default])其中:object是物件;name是物件屬性名字串;default是預設返回值,如果不提供該引數,在沒有對應屬性時,將觸發 AttributeError。
'''

print(getattr(z,"is_z"))
print(getattr(z,"s"))

'''

20
Traceback (most recent call last):
  File "xx.py", line 14, in <module>
    print(getattr(z,"s"))
AttributeError: 'Z' object has no attribute 's'


------------------
(program exited with code: 1)

請按任意鍵繼續. . .


4、setattr
對應函式 getattr(),用於設定屬性值,該屬性必須存在。
格式為setattr(object, name, value)其中:object為物件。name是物件屬性名符串。value為要設定的屬性值。
'''

setattr(z,"is_z",40)
print(z.is_z)

'''

40


------------------
(program exited with code: 0)

請按任意鍵繼續. . .


5、__dict__
此屬性是一個字典型別,用於儲存的物件等的所有值。其實有驗證如下結論。
“1) 內建的資料型別沒有__dict__屬性。
2) 每個類有自己的__dict__屬性,就算存著繼承關係,父類的__dict__ 並不會影響子類的__dict__。
3) 物件也有自己的__dict__屬性, 儲存self.xxx 資訊,父子類物件公用__dict__。 ”
(以上三點引至“//偏執”的博文《Python __dict__屬性詳解》地址:http://www.cnblogs.com/alvin2010/p/9102344.html)
6、type
函式type()如果你只有第一個引數則返回物件的型別,三個引數返回新的型別物件。
isinstance() 與 type() 區別:
type() 不會認為子類是一種父類型別,不考慮繼承關係。
isinstance() 會認為子類是一種父類型別,考慮繼承關係。
如果要判斷兩個型別是否相同最好使用 isinstance()。
格式為type(object)或type(name, bases, dict)其中:object為物件;name為類的名稱;bases 為基類的元組;dict為一個字典,其中包含類內定義的名稱空間變數。一個引數返回物件型別, 三個引數,返回新的型別物件。
7.2.10 抽象基類
抽象類需要藉助模組實現,抽象類是一個特殊的類,它只能被繼承,不能被例項化。它的內部一般規定了方法(包括抽象方法),必須建立一個實現了父類的所有抽象方法的非抽象的子類實現這些方法,才可使用。
抽象類與介面有點類似,但其實是不同的。抽象類是一個介於類和介面直接的一個概念,同時具備類和介面的部分特性,可以用來實現歸一化設計。嚴格的說Python中沒有介面概念。這是因為Python可以實現多重繼承,有抽象類足也。但如果非要弄個“介面”就要呼叫abc模組以實現“介面類”。
①介面:
'''

from abc import abstractmethod,ABCMeta     #這裡是為了實現介面類呼叫的模組
class P(metaclass=ABCMeta):                #在這裡宣告metaclass=ABCMeta
    @abstractmethod                        #然後這裡一個裝飾器abstractmethod,就宣告這個類是介面類
    def pay(self,m):
        pass


        
'''
②抽象類:
'''

import abc                                  #利用abc模組實現抽象類
class F(metaclass=abc.ABCMeta):
    a_t='f'
    @abc.abstractmethod                     #定義抽象方法,無需實現功能
    def r(self):
        pass