1. 程式人生 > 其它 >面向物件之封裝和多型

面向物件之封裝和多型

面向物件

  • 繼承下的派生實際應用
  • 面向物件三大特性之封裝
  • 面向物件三大特性之多型
  • 反射

繼承下的派生實際應用

應用案例

import datetime
import json
d1 = {'t1':datetime.datetime.today(),'t2':datetime.date.today()}
print(d1)
print(json.dumps(d1))  # 不能正常列印,會報錯

解決辦法

注意:可以先檢視json可以序列化的物件 eg:

1.轉字串,暴力直接

d2 = {'t1':str(datetime.datetime.today()),'t2':str(datetime.date.today())}
print(json.dumps(d2))

2.研究json方法,重寫序列化方法

import datetime
import json
class MyJsonEncoder(json.JSONEncoder):
    # 重寫default方法
    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方法繼續執行 防止有其他額外操作)   

面向物件三大特性之封裝

將類中的某些名字隱藏起來 不讓外界直接呼叫

隱藏的目的是為了提供專門的通道去訪問 在通道內可以新增額外的功能

程式碼示例如下:

class Student(object):
    __school = '清華大學'
    def __init__(self, name, age):
        self.__name = name
        self.__age = age
    def check_info(self):
        print("""
        學生姓名:%s
        學生年齡:%s
        """ % (self.__name, self.__age))
    def set_info(self,name,age):
        if len(name) == 0:
            print('使用者名稱不能為空')
            return
        if not isinstance(age,int):
            print('年齡必須是數字')
            return
        self.__name = name
        self.__age = age
stu1 = Student('tony', 18)
stu1.check_info()
stu1.set_info('kevin',28)
stu1.check_info()
stu1.set_info('','nb')
print(stu1.__dict__)
# 通過列印dict就可以看到stu1中的name已經更改

將資料隱藏起來就限制了類外部對資料的直接操作,然後類內應該提供相應的介面來允許類外部間接地操作資料,
介面之上可以附加額外的邏輯來對資料的操作進行嚴格地控制
目的的是為了隔離複雜度,例如ATM程式的取款功能,該功能有很多其他功能組成
比如插卡、身份認證、輸入金額、列印小票、取錢等,而對使用者來說,只需要開發取款這個功能介面即可,其餘功能都可以隱藏起來

property

property就是將方法偽裝成資料

有時候很多資料需要經過計算才可以獲得
但是有些資料給我們的感覺應該屬於資料而不是功能
比如:BMI指數>>>:應該屬於人的資料而不是人的功能

程式碼示例:

1.不偽裝

class Person(object):
    def __init__(self, name, height, weight):
        self.__name = name
        self.height = height
        self.weight = weight
    def BMI(self):
        print('%s的BMI指數是:%s' % (self.__name, self.weight / (self.height ** 2)))
p1 = Person('jason', 1.8, 80)
p1.BMI()

2.偽裝

class Person(object):
    def __init__(self, name, height, weight):
        self.__name = name
        self.height = height
        self.weight = weight
    @property
    def BMI(self):
        return '%s的BMI指數是:%s' % (self.__name, self.weight / (self.height ** 2))
p1 = Person('jason', 1.8, 80)
print(p1.BMI)
p2 = Person('eason',1.9,85)
print(p2.BMI)

面向物件三大特性之多型

什麼是多型: 一種事物的多種形態
eg:
水 固態 液態 氣態
動物 貓、狗、豬

程式碼體現:

class Animal(object):
    def speak(self):
        pass
class Cat(Animal):
    def miao(self):
        print('喵喵喵')
class Dog(Animal):
    def wang(self):
        print('汪汪汪')
class Pig(Animal):
    def heng(self):
        print('哼哼哼')

上述場景下 雖然體現了事物的多型性 但是並沒有完整的體現出來

因為現在不同的形態去叫 需要呼叫不同的方法 不夠一致

只要你是動物 那麼你想要說話 就應該呼叫一個相同的方法 這樣便於管

程式碼體現如下:

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('哼哼哼')

這樣他們呼叫的時候就都是speak方法了

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

雖然python推崇的是自由 但是也提供了強制性的措施來實現多型性
不推薦使用

# 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,無法例項化

鴨子型別

由多型性衍生出一個鴨子型別理論

只要你看著像鴨子 走路像鴨子 說話像鴨子 那麼你就是鴨子!!!

實戰案例:

"""
在linux系統中有一句話>>>:一切皆檔案!!!
    記憶體可以存取資料
    硬碟可以存取資料
    ...
    那麼多有人都是檔案
"""
class Memory(object):
    def read(self):
        pass
    def write(self):
        pass
class Disk(object):
    def read(self):
        pass
    def write(self):
        pass

得到記憶體或者硬碟物件之後 只要想讀取資料就呼叫read 想寫入資料就調

用write 不需要考慮具體的物件是誰

反射

什麼是反射

專業解釋:指程式可以訪問、檢測和修改本身狀態或者行為的一種能力

大白話:其實就是通過字串來操作物件的資料和功能

這裡要注意:在python中一切皆物件

反射需要掌握的四個方法

hasattr():判斷物件是否含有字串對應的資料或者功能

getattr():根據字串獲取對應的變數名或者函式名

setattr():根據字串給物件設定鍵值對(名稱空間中的名字)

delattr():根據字串刪除物件對應的鍵值對(名稱空間中的名字)

反射實際應用

class Student(object):
    school = '清華大學'
    def get(self):
        pass
print(Student.__dict__)
 '''字串的school 跟 變數名school差距大不大?  本質區別'''
  # guess_name = input('請輸入你想要查詢的名字>>>:').strip()
  # 不使用反射不太容易實現
  # print(hasattr(Student, 'school'))  # True
  # print(hasattr(Student, 'get'))  # True
  # print(hasattr(Student, 'post'))  # False

  # print(getattr(Student, 'school'))  # 清華大學
  # print(getattr(Student, 'get'))  # <function Student.get at 0x10527a8c8>

  # 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('類中沒有該名字')
  def index():
      pass
  obj = Student()
  setattr(obj, '血量', 10000)
  setattr(obj, '功能', index)
  print(obj.__dict__)
  delattr(obj, '功能')
  print(obj.__dict__)

setattr

  setattr(Student,'level','貴族學校')
  def index():
      pass
  setattr(Student, '功能', index)

什麼時候使用反射 可以記固定的口訣
以後只要在業務中看到關鍵字
物件 和 字串(使用者輸入、自定義、指定) 那麼肯定用反射

反射案例

# 利用反射獲取配置檔案中的配置資訊
"""一切皆物件 檔案也是物件"""
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()