1. 程式人生 > >初學Python——面向對象(二)

初學Python——面向對象(二)

if else 作用 導入 不同之處 out with fin 進程 rect

一、抽象類、接口類和抽象接口

轉自博客園魏恒https://www.cnblogs.com/weihengblog/p/8528967.html

(一)接口類

  什麽是接口類?在繼承中,我們可以聲明某個子類繼承自某基類,這個基類是個接口類,在接口類中定義了接口名(函數名)且並未實現接口的功能,子類繼承接口類,並實現接口中的功能。這又叫做“接口繼承”。

  接口繼承實質上是規定了一個兼容接口,使得外部調用者無需關心具體細節,可一視同仁的處理實現了特定接口的所有對象”——這在程序設計上,叫做歸一化。特點:1.做出良好的抽象類,2.規定兼容接口 3.調用者可以無需關心具體實現細節,可以一視同仁處理實現特定接口的所有對象。

#做出一個良好的抽象
class Payment(object):
    #規定了一個兼容接口
    def pay(self):
        pass

#微信支付
class WeChatPay(Payment):
    def pay(self,money):
        print(微信支付了%s%money)

#支付寶支付
class AliPay(Payment):
    def pay(self,money):
        print(支付寶支付了%s%money)

#蘋果支付
class ApplePay(Payment):
    def pay(self,money):
        
print(蘋果支付了%s%money) def pay(obj,money): obj.pay(money) weixin = WeChatPay() alipay = AliPay() applepay = ApplePay() #調用者無需關心具體實現細節,可以一視同仁的處理實現了特定接口的所有對象 pay(weixin,100) pay(alipay,200) pay(applepay,300)

(二)抽象類

什麽是抽象類?

  與java一樣,python也有抽象類的概念但是同樣需要借助模塊實現,抽象類是一個特殊的類,它的特殊之處在於只能被繼承,不能被實例化

為什麽要有抽象類?

  如果說類是從一堆對象中抽取相同的內容而來的,那麽抽象類就是從一堆類中抽取相同的內容而來的,內容包括數據屬性和函數屬性。

  比如我們有香蕉的類,有蘋果的類,有桃子的類,從這些類抽取相同的內容就是水果這個抽象的類,你吃水果時,要麽是吃一個具體的香蕉,要麽是吃一個具體的桃子。。。。。。你永遠無法吃到一個叫做水果的東西。

  從設計角度去看,如果類是從現實對象抽象而來的,那麽抽象類就是基於類抽象而來的。

  從實現角度來看,抽象類與普通類的不同之處在於:抽象類中有抽象方法,該類不能被實例化,只能被繼承,且子類必須實現抽象方法。這一點與接口有點類似,但其實是不同的

#一切皆文件
import abc #利用abc模塊實現抽象類

class All_file(metaclass=abc.ABCMeta):
    all_type=file
    @abc.abstractmethod #定義抽象方法,無需實現功能
        def read(self):
        子類必須定義讀功能
            pass

    @abc.abstractmethod #定義抽象方法,無需實現功能
        def write(self):
        子類必須定義寫功能
            pass

# class Txt(All_file):
#     pass
#
# t1=Txt() #報錯,子類沒有定義抽象方法

class Txt(All_file): #子類繼承抽象類,但是必須定義read和write方法
    def read(self):
        print(文本數據的讀取方法)

    def write(self):
        print(文本數據的讀取方法)

class Sata(All_file): #子類繼承抽象類,但是必須定義read和write方法
    def read(self):
        print(硬盤數據的讀取方法)

    def write(self):
        print(硬盤數據的讀取方法)

class Process(All_file): #子類繼承抽象類,但是必須定義read和write方法
    def read(self):
        print(進程數據的讀取方法)

    def write(self):
        print(進程數據的讀取方法)

wenbenwenjian=Txt()

yingpanwenjian=Sata()

jinchengwenjian=Process()

#這樣大家都是被歸一化了,也就是一切皆文件的思想
wenbenwenjian.read()
yingpanwenjian.write()
jinchengwenjian.read()

print(wenbenwenjian.all_type)
print(yingpanwenjian.all_type)
print(jinchengwenjian.all_type)

(三)抽象類和接口類

  抽象類的本質還是類,指的是一組類的相似性,包括數據屬性和函數屬性,而接口強調函數屬性的相似性。

  抽象類是一個介於類和接口直接的一個概念,同時具備類和接口的部分特征,可以用來實現歸一化設計。

二、靜態方法、屬性方法、類方法

1.靜態方法

  @staticmethod

  靜態方法就是一個普通方法,不能訪問實例變量與類變量,與類唯一的聯系就是需要通過類名來調用這個方法,所以
  只是名義上歸類管。私有方法不能作為靜態方法,但受保護類可以作為靜態方法。

class Dog(object):
    def __init__(self,name):
        self.name = name

    @staticmethod # 將eat變成了靜態方法,作用是將此方法斷絕與類的聯系,變普通函數
    def eat(self):
        print(" is eating")

d = Dog("ChenRonghua")
d.eat()
# 這樣運行會出錯,要想不出錯
1.調用時,d.eat(d)。已經斷絕與類的聯系,"self"需要手動傳入
2.eat()方法去掉參數self

2.類方法

  @classmethod

  變成類方法後,方法內只能訪問類變量,不能訪問實例變量。

class Cat(object):
    name = "華仔"
    def __init__(self,name):
        self.name = name
        self.__person = None

    @classmethod # 類方法
    def eat(self,food):
        print({0}吃{1} .format(self.name,food))

c1 = Cat("小貓")

c1.eat("")
# 輸出結果顯示,類方法沒有使用實例變量,使用的是類變量

3.屬性方法

  @property

  可以把一個方法變成靜態屬性。也就是將原本的方法變成了靜態屬性,不能傳參數,不能修改屬性值,也不能刪除

先來看一下

class Cat(object):
    name = "華仔"
    def __init__(self,name):
        self.name = name
        self.person = None

    @property
    def talk(self):
        print("{0} is talking with {1}".format(self.name,self.person))

c1 = Cat("小貓")

c1.talk
#輸出:小貓 is talking with None

  這時,想要對self.person進行修改是不可以的,會報錯。

  若非要修改,也是可以通過特殊方法來修改的,

  #在上述代碼的基礎上加上如下代碼(在原eat方法的下面,類的裏面) 
    @talk.setter   # 修改屬性方法的值需要這樣做
    def talk(self,person):
        print("{0} is talking with {1}".format(self.name,person))
        self.person = person

c1.talk = "Jack"
c1.talk   # 再次訪問,發現真正修改了它
# 輸出:
小貓 is talk with Jack

  同樣,若非要刪除,也可以

    @talk.deleter  # 刪除屬性方法的值需要這樣做
    def talk(self):
        del self.person
        print("已刪除talk")

del c1.talk
c1.talk # 刪除後再訪問運行出錯!

  這時相信你會有個疑問,為什麽還需要屬性方法的存在呢?直接定義一個靜態變量不就行了嗎?屬性方法和屬性有什麽不同呢?

  屬性方法的本質還是方法(函數),只是變得特殊了,我們知道屬性方法具有以下兩點特征:

    1.屬性方法內可以對內部屬性進行訪問

    2.屬性方法不能被隨意修改刪除,防止誤改誤刪,保證了安全性。

  在實際應用中,有這樣的需求:已經寫好了具有特定功能的方法,作用是返回一個數值。而要經過一系列的動作才能得到這個值,這顯然不是變量可以完成的。用戶調用它時只需要像對待靜態變量一樣就可以得到一個值,中間的過程用戶不需要關心。

三、反射

  在實際應用中,我們需要根據用戶輸入來做出相應的工作,比如輸入1調用a方法,輸入2調用b方法,輸入2調用c方法.....,這些我們可以用if else分支語句來判斷並執行,但顯而易見,如果用戶輸入的種類非常多,if else語句的代碼量就非常可觀了,不美觀,也不便於維護。怎麽辦呢?

  1.需要用到反射函數hasattr()來判斷對象是否有這個方法

  2.如果有這個方法,則可以用getatter()函數來找到對象方法的內存地址

  3.加上括號即可調用  

class Dog(object):
    def __init__(self,name):
        self.name = name
    
    def eat(self,name):
        print("{0} is eating {1}".format(self.name,name))

def bulk(self):
    print("{0} is yelling".format(self.name))

d = Dog("Alex")


chioce = input("請輸入操作:").strip()
If hasattr(d,chioce) == True:
    fun = getattr(d,chioce)
    a = input("what does the dog want eating ?")
    fun(a)
else:
    setattr(d,chioce,bulk) # 將類外部的函數加入了類的內部
    v = getattr(d,chioce)
    v(d)

  so,什麽叫反射?通過字符串來運行或修改程序運行時的狀態、屬性、方法

  一共有四個方法:

    1.hasattr(obj,str) obj是對象,str是字符串,判斷對象是否有此方法(屬性),返回True或False

    2.getattr(obj,str) obj是對象,str是字符串,返回對象中方法(屬性)的內存地址

    3.setattr(x,y,z) (x,y=z)x是對象,y是字符串,z是方法的內容,設置一個新的方法(屬性),或修改方法(屬性)

    4.delattr(obj,str) obj是對象,str是字符串,刪除屬性(不能刪除原有的方法?)

chioce = input("請輸入操作:").strip()
setattr(d,chioce,18) # 創建屬性並賦值
print(d.talk)
setattr(d,"name","Chen") # 修改已存在的name屬性
print(d.name)

delattr(d,"name") # 刪除name屬性
#print(d.name)  # 報錯

# 輸入:talk
輸出:
18
Chen
Chen is yelling

四、動態導入模塊

  如何使用字符串導入模塊呢?

  之前講過一個方法,使用__import__()內置函數;第二個方法是動態導入模塊

  • __import__()內置函數:

‘‘‘導入lib包,調用包下aa模塊的text()函數‘‘‘
b = __import__("lib.aa")
b.aa.text()
"這是解釋器內部用的,一般不建議用這個,建議用下面的"
#output:
from lib.aa.text()
  • 動態導入

vimport importlib
c = importlib.import_module("lib.aa")
"導入lib.aa。b 代表 lib.aa "
c.text()
#output:
from lib.aa.text()

五、異常處理

name = ["alex","jack"]
data = {}
try:
    a=5
    f = open("123","r")   #會觸發異常Exception,這個包括了所有個異常
    name[3]         # 會觸發異常indexError
    data["name"]  # 會觸發異常KeyError
except KeyError as e:
    print("沒有此信息",e)
except IndexError as e:
    print("列表索引超出範圍",e)
except Exception as e:
    print("未知錯誤",e)
else: # 一切正常的情況下會執行後面的語句
    print("OK")
finally:
    print("不管有沒有錯誤,都會執行的語句") 
#輸出:

未知錯誤 [Errno 2] No such file or directory: ‘123‘
不管有沒有錯誤,都會執行的語句

  上面的輸出結果是因為,按照執行的順序,首先執行打開文件出現異常,後面的語句沒有被執行的機會。

  想要一條語句抓住兩種錯誤,可以這樣寫:

try:
    name[3]
    data["name"]
except (KeyError,IndexError) as e:
    print("沒有此信息",e)

  能抓住所有錯誤的寫法:

try:
    name[3]
    data["name"]
except Exception as e: # 一勞永逸,無論出現什麽錯誤都會異常處理
    print("出錯了",e)

  有一種錯誤沒辦法抓住:縮進錯誤,例如

try:
    name1 = 10
     b = 20
except Exception as e:
    print("錯誤!")

# output:
 File "F:/Python Files/Learning Log/day7/異常處理.py", line 44
    b = 20
    ^
IndentationError: unexpected indent

斷言:

class A(object):
    def __init__(self,name):
        self.name = name
a = A("Alex")
try:
    assert type(a.name) is str # 意為:斷定A.name的類型是字符串
except AssertionError:
    print("is not str")

  斷言的作用是:可以做一些檢查,增強安全性

初學Python——面向對象(二)