1. 程式人生 > 其它 >繼承下的派生實際應用、面向物件三大特性之封裝、面向物件三大特性之多型、面向物件之反射

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

繼承下的派生實際應用

import datetime
import json
class MyJasonEncoder(json.JSONEncoder):
    def defaule(self,o):
        # 形參o就是即將要被序列化的資料物件
        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.date.today(),'t2':datetime.date.today()}
res = json.dumps(d1,cls=MyJasonEncoder)
print(res)

說明:

方法一:手動將不能序列化的型別先轉化為字串
{'t1': str(datetime.datetime.today()), 't2': str(datetime.date.today())}
方法二:研究json原始碼並重寫序列化方法
研究原始碼發現報錯的方法叫default
raise TypeError("Object of type '%s' is not JSON serializable" % o.__class__.__name__)
方法原理就是寫一個類繼承JSONEncoder然後重寫default

面向物件三大特性之封裝

封裝的含義

"""
將類中的某些名字'隱藏'起來,不讓外界直接呼叫
隱藏的目的為了提供專門的通道去訪問,在通道內可以新增額外的功能
"""
實際上,是把該隱藏的隱藏起來,該暴露的暴露出來;Python只需要將類的成員名為以雙下劃線開頭,就可以隱藏類中的成員。
# 如何封裝名字:
在變數名的前面加上兩個下劃線__
封裝的功能只在類定義階段才能生效
在類中封裝其實也不是絕對的,僅僅是做了語法上的變形狀
__變數名    >>>>    _類名__變數名
#我們雖然指定了封裝的內部變形語法,但是也不能直接去訪問,看到了就表示這個屬性需要通過特定的通道(介面)去訪問

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('jason',18)
stu1.check_info()
stu1.set_info('tuzi',88)
stu1.check_info()
stu1.set_info('','qweqwe')
將資料隱藏起來就限制了類外部對資料的直接操作,然後類內應該提供相應的介面來允許類外部間接的操作資料
介面之上可以附加額外的邏輯來對資料的操作進行嚴格的控制
目的是為了隔離複雜度,例如ATM程式的取款功能,該功能有很多其他功能組成

property

property就是將方法偽裝成資料

property就是將方法偽裝成資料

BMI例項:

有時候很多資料需要經過計算才可以獲得
但是這些資料給我們的感覺應該屬於資料而不是功能
BMI指數>>>:應該屬於人的資料而不是人的功能
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('tuzi', 1.79, 72)
print(p1.BMI)
# tuzi的BMI指數是:22.47120876377142

面向物件三大特性之多型

什麼是多型?

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

多型性

class Animal(object):
    def speak(self):
        pass

class Cat(Animal):
  def speak(self):
      print('喵喵喵')

class Dog(Animal):
  def speak(self):
      print('汪汪汪')

class chicken(Animal):
  def speak(self):
      print('咯咯噠咯咯噠')
上述場景下,不僅僅體現了事物的多型性,但是雖然形態不同,但是動作是一樣的,所以可以呼叫相同的方法(speak)這一個相同的方法,這樣便於管理
c1 = Cat()
d1 = Dog()
p1 = Pig()
c1.speak()
d1.speak()
p1.speak()

面向物件的多型性其實在很早之前就已經接觸過了

eg:

s1 = 'hello world'
l1 = [1, 2, 3, 4]
d1 = {'name': 'jason', 'pwd': 123}
print(len(s1))
print(len(l1))
print(len(d1))
"""
多型性的好處在於增強了程式的靈活性和可擴充套件性,比如通過繼承Animal類建立了一個新的類,例項化得到的物件obj,可以使用相同的方式使用obj.speak()

面向物件的多型性也需要python程式設計師自己去遵守

雖然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 不需要考慮具體的物件是誰

面向物件之反射

反射

什麼是反射

專業解釋:指程式可以訪問、檢測和修改本身狀態或者行為的一種能力
大白話:其實就是通過字串來操作物件的資料和功能

反射需要掌握的四個方法

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

反射實際的應用

setattr(Student,'level','貴族學校')
print(Student.__dict__
def index():
    pass
obj = Student()
setattr(obj, '血量', 10000)
setattr(obj, '功能', index)
print(obj.__dict__)
delattr(obj, '功能')
print(obj.__dict__)

什麼時候用反射

"""
什麼時候使用反射 可以記固定的口訣
以後只要在業務中看到關鍵字 
物件 和 字串(使用者輸入、自定義、指定) 那麼肯定用反射
"""
import settings
dir(settings)
getattr(settings,'NAME')
class FtpServer:
    def serve_forever(self):
        while True:
            inp = input('請輸入你的指令')
            cmd,file = inp.split()
            if hasattr(self,cmd):
                func = getattr(self,cmd)
                func(file)
    def get(self,file):
        print("正在下載%s"%file)

    def put(self,file):
        print('正在上傳%s'%file)

obj = FtpServer()
obj.serve_forever()