1. 程式人生 > 其它 >面向物件程式設計(三)

面向物件程式設計(三)

面向物件程式設計(三)

繼承下的派生實際應用

# 功能:重寫已有方法,在保證原來功能正常的情況下加入自己需要的功能
import datetime
import json

dict1 = {'datetime':datetime.datetime.today(),'time':datetime.date.today()}
# res = json.dumps(dict1)
# print(res)  # Object of type 'datetime' is not JSON serializable

class MyJSONEncoder(json.JSONEncoder):
    def default(self,o):  # 形參o就是即將要被序列化的資料物件
        print('重寫了')
        if isinstance(o, datetime.datetime):
            return o.strftime('%Y-%m-%d %X')  # datetime格式轉成字串
        elif isinstance(o, datetime.date):
            return o.strftime('%Y-%m-%d')  # data時間格式轉成字串
        return super(MyJSONEncoder, self).default(o)  # 重新呼叫父類的方法,防止以前的功能不能用

res = json.dumps(dict1,cls=MyJSONEncoder)
print(res)  # {"datetime": "2022-04-08 16:01:19", "time": "2022-04-08"}

面向物件三大特徵之封裝

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

# 如何封裝
eg:
    class Student(object):
        def __init__(self,name,age):
            self.name = name
            self.age = age

        __school = '家裡蹲大學'  # 名字前面加槓槓'__'即是封裝該名字的意思
        def func1(self):
            print('func1')
        def __func2(self):  # 函式名前面加__就是封裝整個函式
            print('func2')

    obj1 = Student('petter',18)
    # print(obj1.name)  # petter
    # print(obj1.__school)  # AttributeError: 'Student' object has no attribute '__school'
    obj1.func1()  # func1
    obj1.__func2()  # AttributeError: 'Student' object has no attribute '__func2'
 
# 怎麼呼叫封裝資料?
eg:
    class Student(object):
        def __init__(self, name, age):
            self.name = name
            self.age = age

        __school = '家裡蹲大學'  # 名字前面加槓槓'__'即是封裝該名字的意思

        def func1(self):
            print('func1')

        def __func2(self):  # 函式名前面加__就是封裝整個函式
            print('func2')

        def interface_school(self):  # 定義呼叫資料的介面
            print('我來自%s'%self.__school)

        def interface_func2(self):  # 定義呼叫函式的介面
            self.__func2()

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

property(方法偽裝成資料)

eg:
    # 輸入正方形邊長,計算周長和麵積
    class Calculate(object):
        def __init__(self, r):
            self.r = r

        @property
        def perimeter(self):
            return '該正方形的周長是%f' % (self.r * 4)

        @property
        def area(self):
            return '該正方形的面積是%f' % (self.r ** 2)
    obj1 = Calculate(5)
    print(obj1.perimeter)  # 和資料一樣的呼叫方式
    print(obj1.area)
執行結果:
    該正方形的周長是20.000000
	該正方形的面積是25.000000

小發現

def func():
    a = 2
    return '執行結果是%s'%a * 3


print(func())
執行結果:
    執行結果是2執行結果是2執行結果是2
如果return關鍵字後面有運算,要括號括起來,否則會出現多重輸出

面向物件三大特徵之多型

# 什麼是多型?
	一種事物的多種形態
    eg:
        植物>>>草本植物、木本植物、禾本植物
  
# 多型性
	str,dict,tuple,set是不同的資料型別,他們的類也是不同
    但是統計長度的方法都叫len,這就是多型
eg:
    class Cat(object):
        def call(self):
            print('貓叫了')

    class Dog(object):
        def call(self):
            print('狗叫了')

    class Pig(object):
        def call(self):
            print('豬叫了')

    obj1 = Cat()
    obj2 = Dog()
    obj3 = Pig()
    obj1.call()  # 貓叫了
    obj2.call()  # 狗叫了
    obj3.call()  # 豬叫了
'''
有相同操作的類,在定義這種操作時,應統一命名,生成不同的物件後,用不同的物件呼叫同一個方法名實現不同類裡的操作

面向物件的多型性也需要python程式設計師自己去遵守
'''
# 雖然python推崇的是自由 但是也提供了強制性的措施來實現多型性(不推薦使用)
eg:
    import abc


    class Animal(object,metaclass=abc.ABCMeta):
        @abc.abstractmethod  # 這個語法糖使用後,在繼承這個類時,必修要實現下面的方法
        def call(self):
            pass

    class Cat(Animal):
        def aa(self):
            print('aa')
    class Dog(Animal):
        def call(self):
            pass
        def aa(self):
            print('aa')
    # obj1 = Cat()
    # obj1.aa()  # TypeError: Can't instantiate abstract class Cat with abstract methods call
    obj2 = Dog()
    obj2.aa()  # aa
    

面向物件之反射

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

# 反射需要掌握的四個方法
	hasattr():判斷物件是否含有字串對應的資料或者功能
  	getattr():根據字串獲取對應的變數名或者函式名
  	setattr():根據字串給物件設定鍵值對(名稱空間中的名字)
  	delattr():根據字串刪除物件對應的鍵值對(名稱空間中的名字)
      
# 反射的實際應用
# 編寫小程式,判斷物件中是否有指定的名字,有則取出展示
class Student(object):
    school = '家裡蹲大學'

obj1 = Student()
res = input('請輸入欄位').strip()
if hasattr(obj1, res):  # 如果obj1物件裡面有欄位,返回Ture
    name = getattr(obj1, res)  # 獲取字串對應變數名或函式名
    if callable(name):  # 如果可呼叫
        print('類中有一個功能%s'%res,obj1.name())
    else:
        print('類中有一個變數%s'%res,obj1.name)
else:
    print('沒找到')

setattr(obj1, 'name','petter' )  # 建立新名字
delattr(obj1,'school')  # 刪除名字