1. 程式人生 > 其它 >Python封裝,多型,反射

Python封裝,多型,反射

目錄

派生的實際應用

當前需要序列化一個字典,如下程式碼:

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

# 報錯
'''
TypeError: Object of type 'datetime' is not JSON serializable
json不能序列化python所有的資料型別 只能是一些基本資料型別
'''

解決方法:

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

d1={'t1': str(datetime.datetime.today()), 't2': str(datetime.date.today())}
res = json.dumps(d1)
print(res)  # {"t1": "2022-04-08 20:45:06.122407", "t2": "2022-04-08"}

方法2 - 研究json原始碼並重寫序列化方法

'''
研究原始碼發現報錯的方法叫default
        raise TypeError("Object of type '%s' is not JSON serializable" % o.__class__.__name__)
    我們可以寫一個類繼承JSONEncoder然後重寫default方法
'''
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)  # {"t1": "2022-04-08 20:51:41", "t2": "2022-04-08"}

面向物件三大特性之封裝

封裝的含義

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

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

如何封裝名字?

1. 在變數名的前面加上兩個下劃線__,封裝的功能只在類定義階段才能生效!!!
2.在類中封裝其實也不是絕對的,僅僅是做了語法上的變形而已,'__變數名'變成了'_類名__變數名'。
3.雖然指定了封裝的內部變形語法,但是也不能直接去訪問看到了就表示這個屬性需要通過特定的通道(介面)去訪問。

封裝名字演示:

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)  # 報錯,'Student' object has no attribute '__label' 因為隱藏了__label
print(Student.__dict__)  # '_Student__label': '隱藏項!!'
print(Student._Student__label)  # 隱藏項!! 可以通過_類名__變數名 取到值,但是也不能直接去訪問
print(stu1._Student__label)  # 隱藏項!!  表示這個屬性需要通過特定的通道(介面)去訪問

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

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('jasonNB',28)
stu1.check_info()
stu1.set_info('','11')
stu1.check_info()

property偽裝

property就是將方法偽裝成資料。

很多時候有些資料需要通過計算才能獲取,但這些資料給我們的感覺應該是屬於資料而不是功能,這個時候我們就可以用到property來把功能偽裝成資料。

# 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.74, 80)
# p1.BMI()   報錯 'str' object is not callable 已偽裝成資料,不能通過()呼叫。
print(p1.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()  # 哼哼哼

上述場景下雖然體現了事物的多型性 ,但是並沒有完整的體現出來。因為現在不同的形態去叫,需要呼叫不同的方法,不夠一致。只要你是動物,那麼你想要說話,就應該呼叫一個相同的方法 這樣便於管理。

面向物件裡的多型性:len方法

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()。狗,貓,豬都可以呼叫speak()發出自己的叫聲。

強制多型性

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

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

衍生多型性

由多型性衍生出一個鴨子型別理論,只要你看著像鴨子,走路像鴨子,說話像鴨子,那麼你就是鴨子!!!

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():根據字串刪除物件對應的鍵值對(名稱空間中的名字)

四個方法的使用

class Student(object):
    school = '清華大學'
    def get(self):
        pass

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

print(asattr(Student, 'school'))  # True
print(hasattr(Student, 'get'))  # True
print(hasattr(Student, 'post'))  # False

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

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

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

setattr(Student,'隸屬','211和985')
print(Student.__dict__)  #  '隸屬': '211和985'
obj1=Student()
setattr(obj1,'地裡位置','北京市海淀區清華園')
print(obj1.__dict__)  # {'地裡位置': '北京市海淀區清華園'}

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

delattr(obj1,'地裡位置')
print(obj1.__dict__)  # {}

反射實際案例

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