1. 程式人生 > 其它 >學習python,從入門到放棄(27)

學習python,從入門到放棄(27)

學習python,從入門到放棄(27)

繼承下的派生實際應用

import datetime
import json


class MyJsonEncoder(json.JSONEncoder):
    def default(self, o):
        # 形參o就是即將要被序列化的資料物件
        # print('重寫了', o)
        '''將o處理成json能夠序列化的型別即可'''
        if isinstance(o, datetime.datetime):
            return o.strftime('%Y-%m-%d %X')
        elif isinstance(o, datetime.date):
            return o.strftime('%Y-%m-%d')
        return super().default(o)  # 呼叫父類的default(讓父類的default方法繼續執行 防止有其他額外操作)


d1 = {'t1': datetime.datetime.today(), 't2': datetime.date.today()}
res = json.dumps(d1, cls=MyJsonEncoder)
print(res)

json 不能序列化 python 所有的資料型別,只能是一些基本資料型別。所以我們可以使用兩種方法來實現其他的資料型別的序列化。

  1. 手動將不能序列化的型別先轉字串

    {'t1': str(datetime.datetime.today()), 't2': str(datetime.date.today())}
    
  2. 研究json原始碼並重寫序列化方法

    研究原始碼發現報錯的方法叫 default 。

    raise TypeError("Object of type '%s' is not JSON serializable" % o.__class__.__name__)
    

面向物件三大特性之封裝

封裝就是將類中的某些名字'隱藏'起來 不讓外界直接呼叫,隱藏的目的是為了提供專門的通道去訪問,在通道內可以新增額外的功能。

在變數名的前面加上兩個下劃線___變數名即可實現封裝,封裝的功能只在類定義階段才能生效,在類中封裝其實也不是絕對的,僅僅是做了語法上的變形而已,當需要呼叫時,只要使用_類名__變數名的形式即可呼叫。我們雖然指定了封裝的內部變形語法 但是也不能直接去訪問,看到了就表示這個屬性需要通過特定的通道(介面)去訪問。

class Student(object):
    school = '清華大學'
    __label = '逆來順受'  # 由於python崇尚自由 所以並沒有真正的隱藏  而是自動轉換成了特定的語法

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

    def choose_course(self):
        print('%s正在選課' % self.name)


stu1 = Student('jason', 18)
print(stu1.school)  # 清華大學
print(stu1.name)  # jason
print(stu1.age)  # 18
# print(stu1.__label)
print(Student.__dict__)  # '_Student__label': '逆來順受'
print(Student._Student__label)
print(stu1._Student__label)
class Student(object):
    school = '清華大學'
    __label = '逆來順受'  # 由於python崇尚自由 所以並沒有真正的隱藏  而是自動轉換成了特定的語法

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

    def choose_course(self):
        print('%s正在選課' % self.name)


stu1 = Student('jason', 18)
print(stu1.school)  # 清華大學
print(stu1.name)  # jason
print(stu1.age)  # 18
# print(stu1.__label)  # 無法呼叫
print(Student.__dict__)  # '_Student__label': '逆來順受'
print(Student._Student__label)  # 逆來順受
print(stu1._Student__label)  # 逆來順受

將資料隱藏起來就限制了類外部對資料的直接操作,然後類內應該提供相應的介面來允許類外部間接地操作資料,介面之上可以附加額外的邏輯來對資料的操作進行嚴格地控制,目的的是為了隔離複雜度,例如ATM程式的取款功能,該功能有很多其他功能組成。

property

可將方法偽裝成資料。

有時候很多資料需要經過計算才可以獲得,但是這些資料給我們的感覺應該屬於資料而不是功能。

體質指數(BMI)=體重(kg)÷身高^2(m)

BMI 指數,應該屬於人的資料而不是人的功能。

class Person(object):
    def __init__(self, name, height, weight):
        self.__name = name
        self.height = height
        self.weight = weight

    @property
    def BMI(self):
        # print('%s的BMI指數是:%s' % (self.name, self.weight / (self.height ** 2)))
        return '%s的BMI指數是:%s' % (self.__name, self.weight / (self.height ** 2))


p1 = Person('jason', 1.83, 77)
# p1.BMI()  # 22.9
print(p1.BMI)
p2 = Person('eason', 1.90, 85)
# p2.BMI()  # 23.5
print(p2.BMI)
p3 = Person('xd', 1.85, 100)
# p3.BMI()  # 29.2
print(p3.BMI)
p4 = Person('xd', 1.5, 34)
# p4.BMI()  # 15.1
print(p4.BMI)

面向物件三大特性之多型

多型即一種事物的多種形態,比如動物類,可以有貓,狗,豬等等。

一個抽象類有多個子類,因而多型的概念依賴於繼承。

  • 多型性

    多型與多型性並不相同。

    多型性是指具有不同功能的函式可以使用相同的函式名,這樣就可以用一個函式名呼叫不同內容的函式。在面向物件方法中一般是這樣表述多型性:向不同的物件傳送同一條訊息,不同的物件在接收時會產生不同的行為(即方法)。也就是說,每個物件可以用自己的方式去響應共同的訊息。所謂訊息,就是呼叫函式,不同的行為就是指不同的實現,即執行不同的函式。

    class Animal(object):
        def speak(self):
            pass
    
    
    class Cat(Animal):
        def speak(self):
            print('喵喵喵')
    
    
    class Dog(Animal):
        def speak(self):
            print('汪汪汪')
    
    
    class Pig(Animal):
        def speak(self):
            print('哼哼哼')
    
    
    c1 = Cat()
    d1 = Dog()
    p1 = Pig()
    c1.speak()
    d1.speak()
    p1.speak()
    
  • 多型性的好處

    多型性的好處在於增強了程式的靈活性和可擴充套件性,比如通過繼承Animal類建立了一個新的類,例項化得到的物件 obj,可以使用相同的方式使用 obj.speak() 。

  • 注意事項

    # import abc
    # 指定metaclass屬性將類設定為抽象類,抽象類本身只是用來約束子類的,不能被例項化
    # class Animal(metaclass=abc.ABCMeta):
    #     @abc.abstractmethod # 該裝飾器限制子類必須定義有一個名為talk的方法
    #     def talk(self): # 抽象方法中無需實現具體的功能
    #         pass
    # class Person(Animal): # 但凡繼承Animal的子類都必須遵循Animal規定的標準
    #     def talk(self):
    #         pass
    # p1=Person() # 若子類中沒有一個名為talk的方法則會丟擲異常TypeError,無法例項化
    
  • 鴨子理論

    要你看著像鴨子,走路像鴨子,說話像鴨子,那麼你就是鴨子。

    得到記憶體或者硬碟物件之後 只要想讀取資料就呼叫read 想寫入資料就呼叫write 不需要考慮具體的物件是誰。

    class Memory(object):
        def read(self):
            pass
        def write(self):
            pass
    class Disk(object):
        def read(self):
            pass
        def write(self):
            pass
    

面向物件之反射

反射是指程式可以訪問、檢測和修改本身狀態或者行為的一種能力。簡單來說就是通過字串來操作物件的資料和功能。

  • 四個方法

    hasattr():判斷物件是否含有字串對應的資料或者功能
    getattr():根據字串獲取對應的變數名或者函式名
    setattr():根據字串給物件設定鍵值對(名稱空間中的名字)
    delattr():根據字串刪除物件對應的鍵值對(名稱空間中的名字)

  • 實際應用

    判斷 Student 名稱空間中是否含有使用者指定的名字,如果有則取出展示。

    只要在看到關鍵字物件字串(使用者輸入、自定義、指定) 那麼大概率用反射可以完成。

    class Student(object):
        school = '清華大學'
    
        def get(self):
            pass
    
    
    guess_name = input('請輸入你想要查詢的名字>>>:').strip()
    if hasattr(Student, guess_name):
        target_name = getattr(Student, guess_name)
        if callable(target_name):
            print('類中有一個功能名字是%s' % guess_name, target_name)
        else:
            print('類中有一個數據名字是%s' % guess_name, target_name)
    else:
        print('類中沒有該名字')
    
  • 利用反射獲取配置檔案中的配置資訊

    import settings
    
    dir(settings)  # 獲取物件中所有可以使用的名字
    getattr(settings, 'NAME')
    
    
    class FtpServer:
        def serve_forever(self):
            while True:
                inp = input('input your cmd>>: ').strip()
                cmd, file = inp.split()
                if hasattr(self, cmd):  # 根據使用者輸入的cmd,判斷物件self有無對應的方法屬性
                    func = getattr(self, cmd)  # 根據字串cmd,獲取物件self對應的方法屬性
                    func(file)
    
        def get(self, file):
            print('Downloading %s...' % file)
    
        def put(self, file):
            print('Uploading %s...' % file)
    
    
    obj = FtpServer()
    obj.serve_forever()