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

封裝,多型,反射

繼承下的派生實際應用

  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)
  """
  TypeError: Object of type 'datetime' is not JSON serializable
  json不能序列化python所有的資料型別 只能是一些基本資料型別
      json.JSONEncoder

  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__)
      我們可以寫一個類繼承JSONEncoder然後重寫default方法
"""

面向物件-------封裝

簡介

  封裝
  1.封裝是面向物件程式設計的一大特點
  2.面向物件程式設計的第一步 將屬性和方法封裝到一個抽象的類中
  3.外界使用類建立物件,然後讓物件呼叫方法
  4.物件方法的細節都被封裝在類的內部

  說簡單的就是隱藏物件的屬性和實現細節,僅對外提供公共訪問方式。

基礎入門 —> setter、getter

  '''
    封裝就是指隱藏物件中一些不希望外部所訪問到的屬性或方法,即為了保證安全

    如何封裝,或者說如何隱藏屬性或方法?

    在定義類時,將屬性或方法名修改為外部並不知道的名稱,如下定義類
  '''
  class dog:
      def __init__(self,name,age):
          self.name = name
          self.age = age
      def hello(self):
          print(f'大家好,我是{self.name}')

  #例項化一個物件
  d = dog('哈士奇',3)
  d.hello()

  #直接可以修改類中的屬性,不安全
  d.name = '金毛'
  d.hello()

  執行結果
  大家好,我是哈士奇
  大家好,我是金毛

  '''
    那麼我們可以這樣定義,即內部使用hidden_name來定義屬性,外部就不能直接通過name來修改訪問類中的屬性了
  '''
  class dog:
      def __init__(self,name,age):
          self.new_name = name
          self.new_age = age
      def hello(self):
          print(f'大家好,我是{self.new_name}')

  #例項化一個物件
  d = dog('哈士奇',3)
  d.hello()

  #外部不可以修改類中的屬性
  d.name = '金毛'
  d.hello()

  執行結果
  大家好,我是哈士奇
  大家好,我是哈士奇

  '''
    這樣外部雖然不能通過name來修改屬性值,但是還是可以通過new_name來修改屬性值,當然我們是站在上帝的視角來看待這個問題,對於例項化類的物件而言,其並不是完全知道類中的屬性與方法

  那麼,此時物件如何知道和修改屬性的值呢?

  很簡單,在類中定義用於修改和獲取屬性的方法 set_name、get_name

  需要提供一個getter和setter方法使外部可以訪問到屬性

  getter 獲取物件中的指定屬性(get_屬性名)

  setter 用來設定物件的指定屬性(set_屬性名)
  '''
  且看如下程式碼
  class dog:
      def __init__(self,name,age):
          self.new_name = name
          self.new_age = age
      def hello(self):
          print(f'大家好,我是{self.new_name}')

      #用於例項化物件設定修改類屬性的方法
      def set_name(self,name):
          self.new_name = name
      #用於例項化物件獲取得到類屬性的方法
      def get_name(self):
          return self.new_name

  #例項化一個物件
  d = dog('哈士奇',3)
  d.hello()

  #外部使用get_name()方法來修改類中的屬性
  d.get_name()
  #外部使用set_name()方法來修改類中的屬性
  d.set_name('金毛')
  d.hello()

  '''
    這樣做有什麼好處呢?

    使用封裝,確實增加了類的定義的複雜程度,但是它也確保了資料的安全性
  '''
  1.隱藏了屬性名,使呼叫者無法隨意的修改物件中的屬性
  2.增加了getter和setter方法,很好的控制屬性是否是隻讀的
    如果希望屬性是隻讀的,則可以直接去掉setter方法
    如果希望屬性不能被外部訪問,則可以直接去掉getter方法
  3.使用setter方法設定屬性,可以增加資料的驗證,確保資料的值是正確的
    在方法中判斷年齡等屬性的數值是否滿足正常範圍 >0
  4.使用getter方法獲取屬性,使用setter方法設定屬性;可以在讀取屬性和修改屬性的同時做一些其他的
    處理

進階使用 —> 私有屬性

  以上只是根據在面向物件中對封裝的簡單概述,但是在真實環境中用的不是以上的這些方式,用的是Python
中自動的處理封裝的屬性,如下所示
  可以為物件的屬性使用雙下劃線開頭,__xxx(屬性名)
  雙下劃線開頭的屬性,是物件的隱藏屬性,隱藏屬性只能在類的內部訪問,無法通過物件訪問

  其實隱藏屬性只不過是Python自動為屬性改了一個名字

  實際上是將名字修改為了,_類名__屬性名 比如 __name -> _Person__name,這樣還是可以訪問的

  防君子不防小人(並不是絕對不能訪問,如果一定要訪問還是可以通過物件名._類名__屬性名來訪問)

  class dog:
      def __init__(self,name,age):
          self.__name = name
          self.__age = age
      def hello(self):
          print(f'大家好,我是{self.__name},我{self.__age}歲了')

      #用於例項化物件設定修改類屬性的方法
      def set_name(self,name):
          self.__name = name
      #用於例項化物件獲取得到類屬性的方法
      def get_name(self):
          return self.__name


  #例項化一個物件
  d = dog('哈士奇',3)
  d.hello()
  print(d._dog__name)

  # 其實可以通過如下方式修改私有屬性的值,但是不會這麼做,因為外部是不可以修改私有屬性的值的
  d._dog__name = '金毛'
  d._dog__age = '4'
  d.hello()

  執行結果
  大家好,我是哈士奇,我3歲了
  哈士奇
  大家好,我是金毛,我4歲了

強化加強 —> property

  property裝飾器,用來將一個get方法,轉換為物件的屬性

  新增為property裝飾器以後,我們就可以像呼叫屬性一樣使用get方法

  使用屬性的方式去呼叫方法,看上去是對屬性賦值,但是本質上是呼叫方法的

  使用property裝飾的方法,必須和屬性名是一樣的

  設定setter前必須設定getter

  class dog:
      def __init__(self,name,age):
          self._name = name
          self._age = age
      def hello(self):
          print(f'大家好,我是{self._name},我{self._age}歲了')

      @property
      def name(self):
          print('get方法執行了~~~')
          return self._name
      # setter方法的裝飾器:@屬性名.setter
      @name.setter
      def name(self,name):
          print('set方法執行了~~~')
          self._name = name

      @property
      def age(self):
          print('get方法執行了~~~')
          return self._age
      # setter方法的裝飾器:@屬性名.setter
      @age.setter
      def age(self,age):
          print('set方法執行了~~~')
          self._age = age

  #例項化一個物件
  d = dog('哈士奇',3)
  d.hello()
  print('--'*30)

  # 呼叫set方法,修改name的值;注意看上去是修改屬性名的方式,其實是呼叫的了方法,注意看輸出
  d.name = '德牧'
  d.hello()
  print('--'*30)

  d.age = 4
  d.hello()
  print('--'*30)
  print(d.name)

  # 執行結果
  # 大家好,我是哈士奇,我3歲了
  # ------------------------------------------------------------
  # set方法執行了~~~
  # 大家好,我是德牧,我3歲了
  # ------------------------------------------------------------
  # set方法執行了~~~
  # 大家好,我是德牧,我4歲了
  # ------------------------------------------------------------
  # get方法執行了~~~
  # 德牧

面向物件-------多型

  多型顧名思義多種狀態,在python中,不同的物件呼叫同一個介面,表現出不同的狀態,稱為多型。

  要實現多型有兩個前提:
  1.繼承:多型必須發生在父類與子類之間
  2.重寫:子類重寫父類方法
  
  例:
  class Animal():
      def who(self):
          print("I am an Animal")
  class Duck(Animal):
      def who(self):
          print("I am a duck")

  class Dog(Animal):
      def who(self):
          print("I am a dog")

  class Cat(Animal):
      def who(self):
          print("I am a cat")
  if __name__ == "__main__":
      duck=Duck()
      dog=Dog()
      cat=Cat()
      duck.who()
      dog.who()
      cat.who()
  '''多型的使用增強了程式的靈活性和可擴充套件性'''

反射

  hasattr():判斷物件是否含有字串對應的資料或者功能
  getattr():根據字串獲取對應的變數名或者函式名
  setattr():根據字串給物件設定鍵值對(名稱空間中的名字)
  delattr():根據字串刪除物件對應的鍵值對(名稱空間中的名字)
  class Hero(object):
      def __init__(self,name,blood):
          self.name = name
          self.blood = blood
          @property
      def get_name(self):
          return self.name
      @get_name.setter
      def set_name(self,name):
          self.name = name
  hero = Hero('卡莎',1000)


  1.hasattr(object,name)
    判斷object中有沒有name字串對應的方法和屬性,name可以是函式也可以是引數,還可以是變數,返回結
    果為True、False
    print(hasattr(hero,'name')) # True

  2.getattr(object,name,default=None)
    獲取name屬性,如果沒有會報錯,但是新增default屬性後,沒有name會直接列印default值
    print(getattr(hero,'get_name')) # 卡莎
    print(getattr(hero,'f1')) # <bound method Hero.f1 of <__main__.Hero object at 0x01E6D1C0>>

  3.setattr(object,屬性名,屬性值)
    返回值是None
    設定屬性物件的屬性,還可以直接修改當前物件的屬性
    print(setattr(hero,'name','女警')) # None
    print(hero.name) # 女警

  4.delattr(object,屬性名)
    刪除物件屬性
    print(hero.__dict__) # {'name': '卡莎', 'blood': 1000}
    delattr(hero,'name')
    print(hero.__dict__) # {'blood': 1000}