天天看點

Python基礎之面向對象面向對象

面向對象

1、面向對象VS面向過程

1. 程式設計範式

  • 1.程式設計是程式員用特定的文法+資料結構+算法組成的代碼來告訴計算機如何執行任務的過程
  • 2.實作一個任務的方式有很多種不同的方式,對這些不同的變成方式的特點進行歸納總結得出來的程式設計方式類别,即程式設計範式
  • 3.兩種最重要的變成範式分别是面向過程程式設計和面向對象程式設計

2.面向過程程式設計(Procedurak Programming)

  • 1.面向過程程式設計是程式從上到下一步步執行,一步步從上到下,從頭到尾的解決問題
  • 2.基本設計思路就是程式一開始是要着手解決一個大的問題,然後把一個大問題分解成很多個小問題或子過程
  • 3.這些子過程再執行的過程再繼續分解直到小問題足夠簡單到可以在一個小步驟範圍内解決
  • 4.這樣做的問題也是顯而易見的,就是如果你要對程式進行修改,對你修改的那部分有依賴的各個部分你都也要跟着修改
  • 如果程式開頭你設定了一個變量值為1,但如果其他子過程依賴這個值為1的變量才能正常運作,那麼這個變量1變了依賴程式全都要改變
  • 代碼示例
    def db_conn():
        print("connecting db...")
    
    def db_backup(dbname):
        print("導出資料庫...", dbname)
        print("将備份檔案打包,移至相應目錄...")
    
    def db_backup_test():
        print("将備份檔案導入測試庫,看導入是否成功")
    
    def main():
        db_conn()
        db_backup('my_db')
        db_backup_test()
    
    if __name__ == '__main__':
        main()
    
    # 運作結果:
    # connecting db...
    # 導出資料庫... my_db
    # 将備份檔案打包,移至相應目錄...
    # 将備份檔案導入測試庫,看導入是否成功
               

2、面向對象程式設計主要優點、公有性、私有屬性,普通屬性等

2.1、面向對象程式設計主要優點:易維護,易擴充,效率高

  • 1.其實OOP程式設計的主要作用和函數一樣也是使你的代碼修改和擴充邊的更容易
  • 2.函數程式設計與OOP的主要差別在于OOP可以使程式更加容易擴充和易更改
  • 3.OOP程式設計是利用"類"和"對象"來建立各種模型來實作對真實世界的描述
  • 4.使用面向對象程式設計的原因一方面是因為它可以使程式維護和擴充變得更簡單,并且可以大大提高程式開發效率
  • 5.另外,基于面向對象的程式可以使它他人更加容易了解你的代碼邏輯,進而使團隊開發變得更從容

2.2、Class類

  • 1.一個類即是對一類擁有相同屬性的對象的抽象、藍圖、原型
  • 2.在類中定義了這些對象的鬥具備的屬性(variables(data))、共同的方法

2.3、Object對象(執行個體)

  • 1.一個對象即是一個類的執行個體化後的執行個體
  • 2.一個類必須經過執行個體化後方可在程式中調用,一個類可以執行個體化多個對象,每個對象亦可以有不同的屬性
  • 3.就像人類是指所有人,每個人是指具體的對象,人與人之前有共同性,亦有不同

2.4、類中一些名詞

  • 1.init :構造函數
  • 2.Self.name = name:執行個體變量,普通屬性或者叫普通字段
  • 3.public_object = “public”:類變量、公有屬性或者叫靜态字段
  • 4.self.__heart = “Normal”:私有屬性在外面則無法通路
  • 5.def shot(self):類方法
    class Role(object):    #在定義類時繼承object就是新式類,沒有就是就是舊類式
        public_object = "public"   #在類例定義一個公有屬性:所有執行個體都能通路的屬性叫“公有屬性”
        def __init__(self,name,role,weapon,life_value=100,money=15000):  #構造函數==初始化方法:模闆
            self.name = name    #普通屬性
            self.role = role
            self.weapon = weapon
            self.life_value = life_value
            self.money = money
            self.__heart= "Normal"    #私有屬性在外面無法通路
        def shot(self):     #類的方法
            print("%s is shooting..."%self.name)
        def got_shot(self):
            print("ah...,I got shot...")
        def buy_gun(self,gun_name):
            print("%s just bought %s" %(self.name,gun_name))
            self.weapon = gun_name          #在購買後讓執行個體值改變
    
    #在下面執行個體化其實就是傳入: Role('r1','Alex','police','AK47') 把r1傳給了self
    #r1就是執行個體化後産生的目前Role類的執行個體,是以self就是執行個體本身
    #我了解r1其實就是__init__函數的記憶體位址
    #是以在下面函數中調用self.name就相當于調用r1.name是以可以調用
    r1 = Role('Alex','police','AK47') #生成一個角色  隻要一執行個體化就會自動調用__init__
    r2 = Role('Jack','terrorist','B22')  #生成一個角色
    
    r1.shot()
    print(r2.weapon)        #在調用r2.buy_gun('AK47')前是:B22
    r2.buy_gun('AK47')
    print(r2.weapon)        #在調用r2.buy_gun('AK47')後是:AK47
    
    #私有屬性
    # print(r2.__heart)     #這裡的.__heart是私有屬性,是以在外部無法通路
    print(r2._Role__heart)  #強制通路私有屬性的方法
    
    #公有屬性
    print(r2.public_object) #列印出類的公有屬性
    Role.public_object = 'change_public'    #從全局改變類的公有屬性 r1,r2的公有屬性都會變
    print(r2.public_object)  #這裡列印可以看出公有屬性變成類“change_public"
    r2.public_object = "public_r2"          #改變r2對象的公有屬性,r1不會變
    print(r2.public_object)                  #列印出改變後的r2公有屬性
               

2.5、公有屬性、普通屬性、私有屬性比較

  • 1.公有屬性:在記憶體中僅存一份
  • 2.普通屬性:每個執行個體對象在記憶體存一份
  • 3.私有屬性:執行個體在外部無法調用

2.6、類中函數私有化

  • 1.預設情況下,程式可以從外部通路一個對象的特性
  • 2.為了讓方法和特性變成私有(從外部無法通路),隻要在它的名字前加上雙下劃線即可
  • 3.先在__inaccessible從外界是無法通路的,而在内部還能使用(如從accessible通路)
  • 類中函數私有化
    • class Secretive:
          def __accessible(self):
              print("you can't see me,unless you're calling internally")
          def accessible(self):
              print("The secret message is:")
              self.__accessible()
      s = Secretive()
      s.accessible()
      
      # 運作結果:
      # The secret message is:
      # you can't see me,unless you're calling internally
                 

3、面向對象的三大特性:封裝、繼承、多态

3.1、Encapsulation 封裝(隐藏實作細節)

  • 1.在類中對資料的指派、内部調用對外部使用者是透明的
  • 2.在使這類變成了一個膠囊或容器,裡面包含着類的資料和方法
  • 3.作用:
    • 防止資料被随意修改
    • 使外部程式不需要關注對象内部的構造,隻需要通過對外提供的接口進行直接通路

3.2、Inheritance(代碼重用)

  • 1.一個類可以派生出子類,在這個父類裡定義的屬性、方法自動被子類繼承
  • 2.比如CS中的警察和恐怖分子,可以将兩個角色的相同點寫到一個父類中,然後同時去繼承它
  • 3.使用經典類:Person.init(self,name,age)并重寫寫父類Person的構造方法中,實作。先覆寫,再繼承,最後構造
    • class Person(object):
          def __init__(self,name,age):  #執行Person.__init__(self,name,age)時就會将傳入的參數執行一遍
              self.name = name          #是以在BlackPerson中不僅有name,age而且還有sex
              self.age = age
              self.sex = "normal"
          def talk(self):
              print("person is talking....")
      
      class WhitePerson(Person):
          pass
      
      class BlackPerson(Person):
          def __init__(self,name,age,strength):     #先覆寫,再繼承,再重構
              #先覆寫父類的__init__方法,再繼承父類__init__,再加自己的參數
              Person.__init__(self,name,age)        #先繼承父類Person,這裡self就是BlackPerson本身
              #先将name,age傳給子類BlackPerson,然後調用Person.__init__構造方法将參數出入父類()
              self.strength = strength              #然後再重構自己的方法,即寫自己的參數
              print(self.name,self.age,self.sex)
              print(self.strength)
          def talk(self):
              print("black balabla")
          def walk(self):
              print("is walking....")
      
      b = BlackPerson("wei er smith",22,"Strong")
      b.talk()
      b.walk()
      
      
      # 運作結果:
      # wei er smith 22 normal
      # Strong
      # black balabla
      # is walking....
      # person is talking....
                 

3.3Polymorphism多态(接口重用)

  • 1.多态是面向對象的重要特性,簡單點說:“一個接口,多種實作”
  • 2.指一個基類中派生出了不同的子類,且每個子類在繼承同樣的方法名的同時又對父類的方法做了不同的實作
  • 3.這就是同一種事務表現出的多種形态
  • 比如黃種人繼承了talk這個功能,但是他說的是中文,而美國人的talk是英文,但是他們是同樣的talk
  • 作用:簡單說就是允許父類調用子類的方法
    • 例如:
      class Animal:
          def __init__(self, name):    # Constructor of the class
              self.name = name
          def talk(self):              # Abstract method, defined by convention only
              raise NotImplementedError("Subclass must implement abstract method")
      
      class Cat(Animal):
          def talk(self):
              return 'Meow!'
      
      class Dog(Animal):
          def talk(self):
              return 'Woof! Woof!'
      
      animals = [Cat('Missy'),
                 Dog('Lassie')]
      
      for animal in animals:
          print(animal.name + ': ' + animal.talk())
      
      # 運作結果:
      # Missy: Meow!
      # Lassie: Woof! Woof!
                 

4、面向對象:組合——功能類似繼承

4.1、組合的作用

  • 1.和繼承很相似,可以讓在一個類中使用另一個類的方法,但是并不是父類和子類的繼承關系
  • 2.他是先執行個體化一個類的執行個體,然後将這個執行個體當做參數傳入另一個類中,所有就可以在這個類中調用了

4.2、組合舉例說明

  • 1.如何實作Teacher類不是SchoolMember類的子類的情況下而在Teacher類中調用SchoolMember的方法
  • 2.方法很簡單:首先執行個體化一個SchoolMember的執行個體,然後将這個執行個體當做參數傳入到Teacher類中
  • 3.那麼在Teacher類中通過 ***.fangfa就可以調用了
  • 4.這裡T1.user_SM()就是調用了SchoolMember中的tell方法
    • 例:
      class SchoolMember(object):
          def __init__(self,name,age):
              self.name = name
              self.age = age
          def tell(self):
              print("I am SchoolMember,my name is %s"%self.name)
      SM1 = SchoolMember("tom",100)
      
      class Teacher(object):
          def __init__(self,sex,t1):
              self.SchoolMember = t1
          def user_SM(self):
              self.SchoolMember.tell()
      T1 = Teacher('F',SM1)
      T1.user_SM()
                 

5、建立一個類,并執行個體化後在記憶體中怎樣存儲的

5.1、建立一個最簡單的Dog類,執行個體化并調用它的sayhi()方法,這個過程的原理

  • class Dog(object):
        def __init__(self,name,dog_type):
            self.name = name
            self.type = dog_type
        def sayhi(self):
            print("i am a dog,my name is %s"%self.name)
    d = Dog('LiChuang','京巴')   #等價于  Dog(d,"LiChuang","京巴")
    d.sayhi()                   #運作結果:i am a dog,my name is LiChuang
               

5.2、為何說呢麼可以在sayhi()類方法中可以調用self.name

  • 1.在執行個體化時:d = Dog(“LiChuang”,“京巴”)等價于d=Dog(d,“Lichuang”,‘京巴’)将執行個體d自己傳遞給類中self參數,是以self.name=name 等價于d.name=name
  • 2.在調用類方法d.sayhi()時相當于調用d.sayhi(d),上一步執行個體化時已經将d.name = name(指派給了name)
  • 3.是以在類方法中調用self.name就是調用d.name, self傳遞給了sayhi()是以可以調用self.name

5.3、執行個體化d = Dog(“Lichuang”,“京巴”)時在記憶體中時怎樣存儲的?

  • 1.當一個類被定義後,就會開辟一塊記憶體空間,存放着類自己(可以了解為一個模闆)
  • 2.執行d = Dog(‘LiChuang’,‘京巴’)時是實質上需要執行以下幾步:
    • 第一步:先申請一塊記憶體空間,命名為d
    • 第二步:将第一步中d的位址空間,d執行個體和‘LiChuang’ ‘京巴’兩個參數都傳遞給類自己(也就是模闆)
    • 第三步:剛剛将執行個體d已經傳進去了,是以可以執行d.name = LiChuang, d.type = 京巴,然後将這兩個值傳回到剛剛執行個體d申請的記憶體中
    • Python基礎之面向對象面向對象

6、新式類 與 經典類 及 重寫構造方法 差別舉例

6.1、新式類和經典類差別(三種)

  • 1.首先,寫法不一樣:

    class A: #經典類寫法

      pass

    class B(object): #新式類寫法

      pass

  • 2.多繼承中,新式類采用廣度優先搜尋,而舊式類是采用深度優先搜尋,python3中全是廣度查詢
  • 在繼承中新式類和經典類寫法差別

    SchoolMember.init(self,name,age,sex) #經典類寫法

    super(Teacher,self).init(name,age,sex) #新式類寫法

    Python基礎之面向對象面向對象

6.2重寫特殊的構造方法

  • 1.注:如果一個類的構造方法被重寫,那麼就需要調用超類的構造方法,否則對象可能不能給被正确的初始化
  • 2.其實調用超類構造方法很容易,SongBird類中隻添加一行代碼

     經典類寫法: Bird.init(self)

     新式類寫法: super(SongBird,self).init()

  • 3.如果将下面代碼沒有2中的那句調用父類Bird中的self.hungry方法會報錯

    if self.hungry == True:

    AttributeError: SongBird instance has no attribute ‘hungry’

7、靜态方法、類方法、屬性方法

7.1、靜态方法

  • 1.作用:靜态方法可以更好的組織代碼,防止代碼變大後變得比較混亂。
  • 2.特性: 靜态方法隻是名義上歸類管理,實際上在靜态方法裡通路不了類或則執行個體中的任何屬性
  • 3.靜态方法使用場景:

    1)我們要寫一個隻在類中運作而不在執行個體中運作的方法.

    2)經常有一些跟類有關系的功能但在運作時又不需要執行個體和類參與的情況下需要用到靜态方法.

    3)比如更改環境變量或者修改其他類的屬性等能用到靜态方法.

    4)這種情況可以直接用函數解決, 但這樣同樣會擴散類内部的代碼,造成維護困難.

  • 4.調用方式: 既可以被類直接調用,也可以通過執行個體調用
    class Dog(object):
        def __init__(self,name):
            self.name = name
        @staticmethod
        def eat():
            print("I am a static method")
    d = Dog("ChenRonghua")
    d.eat()                       #方法1:使用執行個體調用
    Dog.eat()                   #方法2:使用類直接調用
               

7.2、類方法

  • 1.作用:無需執行個體化直接被類調用
  • 2.特性: 類方法隻能通路類變量,不能通路執行個體變量
  • 3.類方法使用場景: 當我們還未建立執行個體,但是需要調用類中的方法
  • 4.調用方式: 既可以被類直接調用,也可以通過執行個體調用
    class Dog(object):
        name = '類變量' #在這裡如果不定義類變量僅定義執行個體變量依然報錯
        def __init__(self,name):
            self.name = '執行個體變量'
            self.name = name
        @classmethod
        def eat(self,food):
            print("%s is eating %s"%(self.name,food))
    Dog.eat('baozi')                   #方法1:使用類直接調用
    d = Dog("ChenRonghua")          
    d.eat("包子")                      #方法2:使用執行個體d調用
               

7.3、屬性方法

  • 1.作用:屬性方法把一個方法變成一個屬性,隐藏了實作細節,調用時不必加括号直接d.eat即可調用self.eat()方法
  • 2.例如:
    class Dog(object):
        def __init__(self, name):
            self.name = name
    
        @property
        def eat(self):
            print(" %s is eating" % self.name)
    d = Dog("ChenRonghua")
    d.eat()
    # 調用會出以下錯誤, 說NoneType is not callable, 因為eat此時已經變成一個靜态屬性了, 
    # 不是方法了, 想調用已經不需要加()号了,直接d.eat就可以了
               

8、類的特殊成員方法:str、__call__等

8.1、__doc__表示類的描述資訊

class Foo:
    """ 輸出類的描述類資訊 """
    def func(self):
        pass
print(Foo.__doc__) #運作結果:描輸出類的描述類資訊
           

8.2、__call__對象後面加括号,觸發執行

  • 作用:構造方法__init__的執行是由建立對象觸發的,即:對象 = 類名() ;而對于 call 方法的執行是由對象後,括号觸發的,即:對象() 或者 類()() 執行__call__方法
class Dog(object):
    def __init__(self,name):
        self.name = '執行個體變量'
        self.name = name
    def __call__(self, *args, **kwargs):
        print("running call")
        print(args,kwargs)
d = Dog("ChenRonghua")
d(name='tom')               # 如果隻執行個體化,而不d()調用,__call__函數不會執行

# 運作結果:
# running call
# () {'name': 'tom'}
           

8.3、__str__如果一個類中定義了__str__方法,在列印對象時,預設輸出該方法的傳回值

class Foo:
    def __str__(self):
        return 'alex li'
obj = Foo()
print(obj)     # 輸出:alex li
           

8.4、 dict 檢視類或對象中的所有成員

class Province:
    country = 'China'
    def __init__(self, name, count):
        self.name = name
        self.count = count
    def func(self, *args, **kwargs):
        print('func')
# 擷取類的成員,即:靜态字段、方法、
print(Province.__dict__)
# 輸出:{'country': 'China', '__module__': '__main__', 'func': <function func at 0x10be30f50>, '__init__': <function __init__ at 0x10be30ed8>, '__doc__': None}

obj1 = Province('HeBei',10000)
print(obj1.__dict__)
# 擷取 對象obj1 的成員
# 輸出:{'count': 10000, 'name': 'HeBei'}

obj2 = Province('HeNan', 3888)
print(obj2.__dict__)
# 擷取 對象obj1 的成員
# 輸出:{'count': 3888, 'name': 'HeNan'}
           

8.5、getitem、setitem、delitem

  • 作用:用于索引操作,如字典。以上分别表示擷取、設定、删除資料
class Foo(object):
    def __getitem__(self, key):
        print('__getitem__', key)

    def __setitem__(self, key, value):
        print('__setitem__', key, value)

    def __delitem__(self, key):
        print('__delitem__', key)

obj = Foo()
result = obj['k1']           # __getitem__ k1
obj['k2'] = 'wupeiqi'        # __setitem__ k2 wupeiqi
del obj['k1']                # __delitem__ k1
           

8.6、new 、 metaclass

  • 作用: __metaclass__定義這個類以怎樣的形式被建立
class User(object):
    def __init__(self,name,age):
        print('__init__')

    def __new__(cls, *args, **kwargs):
        print("__new__",args,kwargs)
        return object.__new__(cls)         # 調用一下object的__new__方法否則不往下走
d = User('tom',100)

# 運作結果:
# __new__ ('tom', 100) {}
# __init__
           

8.7、__new__和__init__的差別

  • 1、 __new__是一個靜态方法,而__init__是一個執行個體方法.
  • 2、 __new__方法會傳回一個建立的執行個體,而__init__什麼都不傳回.
  • 3、 隻有在__new__傳回一個cls的執行個體時後面的__init__才能被調用.
  • 4、 當建立一個新執行個體時調用__new__,初始化一個執行個體時用__init__.

9、type生成類(metaclass)

9.1、type生成類調用順序

  • 1) new : 先于__init__方法,每生成一個執行個體執行一次
  • 2) init : __init__方法每生成一個執行個體對象就會執行一次
  • 3) call : 後與__init__方法,C()() 使用類再加一個括号調用, C為類名稱
  • 4)del : 析構方法,删除無用的記憶體對象(當程式結束會自動自行析構方法)
  • class Student(object):
        def __new__(cls, *args, **kwargs):
            print('__new__')
            return object.__new__(cls)    # 必須傳回父類的__new__方法,否則不不執行__init__方法,無法建立執行個體
    
        def __init__(self,name):
            print('__init__')
            self.name = name
    
        def __str__(self):                # 作用:列印執行個體時顯示指定字元串,而不是記憶體位址
            print('__str__')
            return self.name
    
        def __call__(self, *args, **kwargs):        # 當執行C()(*args) 或者 s1(*args) 就會執行__call__
            print('__call__',*args)
    
        def __del__(self):                # 作用:清除無用的執行個體對記憶體的暫用
            print('__del__')
    
    #1、執行個體化時機會執行__new__、__init__
    s1 = Student('tom')
    
    #2、執行 執行個體()  就會執行__call__ 方法,并将參數傳遞給__call__函數
    s1('call01')
    
    #3、當列印執行個體時就會執行 __str__ 方法下傳回的字元串(預設傳回的執行個體位址)
    print(s1)
    
    #4、析構方法:當删除執行個體時就會調用 __del__ 方法
    del s1
    # 析構方法作用:在程式結束後會自動執行析構方法删除所有執行個體
    # 但是在程式運作時有很多執行個體是無用的,但是python記憶體回收機制卻不會自動删除他們,這樣就浪費記憶體
    # 我們可以執行 del s1 ,那麼在程式運作時,python記憶體回收機制會檢測到這些執行個體時無用的,才會删除
    # 其實我們執行del s1,并沒有回收記憶體,隻不過是摘除門牌号,python記憶體回收機制發現沒有門牌号後會自動回收記憶體
               

9.2、普通方式建立類過程了解

class Foo(object):
    def __init__(self,name):
        self.name = name
f = Foo("alex")
           
  • 1.上述代碼中,obj 是通過 Foo 類執行個體化的對象,其實,不僅 obj 是一個對象,Foo類本身也是一個對象
  • 2.如果按照一切事物都是對象的理論:obj對象是通過執行Foo類的構造方法建立,那麼Foo類對象應該也是通過執行某個類的 構造方法 建立。
    • print type(f) # 輸出:<class ‘main.Foo’> 表示,obj 對象由Foo類建立

      print type(Foo) # 輸出:<type ‘type’> 表示,Foo類對象由 type 類建立

  • 3.是以,f對象是Foo類的一個執行個體,Foo類對象是 type 類的一個執行個體,即:Foo類對象 是通過type類的構造方法建立。
  • 4.那麼問題來了,類預設是由 type 類執行個體化産生,type類中如何實作的建立類?類又是如何建立對象?
  • 5.答:類中有一個屬性 metaclass,其用來表示該類由 誰 來執行個體化建立,是以,我們可為 __metaclass__設定一個type類的派生類,進而檢視 類 建立的過程
  • Python基礎之面向對象面向對象

9.3、type建立無構造方法的類

def func(self):
    print('hello world')
Foo = type('Foo', (object,), {'func': func,'city':'bj'})

obj = Foo()
obj.func()                    # hello wupeiqi
print(obj.city)              # bj

# type第一個參數:類名
# type第二個參數:目前類的基類(繼承的類)
# type第三個參數:類的成員(比如内中有的方法)


#注:上面使用type生成的類就是這樣的
class Foo(obj):
    city = 'bj'
    def func(self):
        print('hello world')
           

9.4、type建立有構造方法的類

def __init__(self,name,age):
    self.name = name
    self.age = age

def sayName(self):
    print("hello I am %s"%self.name)

def sayAge(self):
    print("hello I am %s"%self.age)

Foo = type('Foo',(object,),{'sayName':sayName,'__init__':__init__})
# type第一個參數:類名
# type第二個參數:目前類的基類(繼承的類)
# type第三個參數:類的成員(比如内中有的方法)

setattr(Foo,'sayAge',sayAge)        # 使用setattr添加一個類方法

f = Foo("jack",22)
f.sayName()               # hello I am jack
f.sayAge()                # hello I am 22

#注:上面使用type生成的類就是這樣的
class Foo(object):
    def __init__(self,name,age):
        self.name = name
        self.age = age

    def sayName(self):
        print("hello I am %s" % self.name)

    def sayAge(self):
        print("hello I am %s" % self.age)
           

9.5、使用type動态生成ModelForm類舉例

def create_model_form(request,admin_class):
   def __new__(cls,*args,**kwargs):                        #繼承ModelForm的__new__方法
      pass
   def default_clean(self):
      pass
   class Meta:                                             #ModelForm中使用Meta類進行條件過濾
      model = admin_class.model                            #model指定關聯那個類
      fields = "__all__"                                  #僅顯示那些字段
      exclude = admin_class.modelform_exclude_fields       #那些字段不顯示
   attrs = {'Meta':Meta}

   #這個生成的ModelForm類繼承ModelForm類,可以用來做ModelForm驗證
   _model_form_class = type("DynamicModelForm",(ModelForm,),attrs)       #建立類并設定Meta屬性
   setattr(_model_form_class,"__new__",__new__)                           #動态将__new__函數添加到類中
   setattr(_model_form_class,'clean',default_clean)                       #動态将_default_clean__函數添加到類中
           

10、反射: hasattr、getattr、setattr 和 delattr

10.1、hasattr(ogj,name_str) 判斷一個對象裡是否有對應的字元串方法

class Dog(object):
    def eat(self,food):
        print("eat method!!!")
d = Dog()

#hasattr判斷對象d是否有eat方法,有傳回True,沒有傳回False
print(hasattr(d,'eat'))     #True
print(hasattr(d,'cat'))     #False
           

10.2、getattr(obj,name_str) 根據字元串去擷取obj對象裡的對應的方法對應的記憶體位址

class Dog(object):
    def eat(self):
        print("eat method!!!")
d = Dog()

if hasattr(d,'eat'):          # hasattr判斷執行個體是否有eat方法
    func = getattr(d, 'eat')  # getattr擷取執行個體d的eat方法記憶體位址
    func()                    # 執行執行個體d的eat方法
#運作結果:  eat method!!!
           

10.3、使用stattr給類執行個體對象動态添加一個新的方法、屬性

使用stattr給類執行個體對象動态添加一個新的方法

class Dog(object):
    def eat(self,food):
        print("eat method!!!")
d = Dog()

def bulk(self):               #給Dog類添加一個方法必須先寫這個方法
    print('bulk method add to Dog obj')

d = Dog()
setattr(d,"bulk",bulk)        #将bulk方法添加到執行個體d中,命名為talk方法
d.bulk(d)                     #執行個體d調用剛剛添加的talk方法時必須将執行個體d自身當變量傳入,因為他不會自己傳入self

#1. 注:setattr(x,’y’,z)用法:    x就是執行個體對象  y就是在執行個體中調用時用的名字,z就是改變屬性的值/或則要添加的函數的名字(正真的函數)
#2. setattr( 具體執行個體名稱 , ’在類中調用時使用的名稱’ , 要添加的真實函數的名稱)
#3. 作用: setattr(d,"bulk",bulk) 将bulk方法添加到執行個體d中并且在執行個體中以bulk名稱調用
           

使用stattr給類執行個體對象動态添加一個新的屬性

class Dog(object):
    def __init__(self,name):
        self.name = name
    def eat(self,food):
        print("eat method!!!")
d = Dog('Fly')

#1 執行個體d中沒有sex這個屬性,就會動态添加一個屬性   sex = Male
setattr(d,"sex",'Male')   #給執行個體d添加一個屬性:sex=Male
print("将執行個體d添加一個新屬性sex=Male:\t",d.sex)

#2 如果執行個體d中本身存在這個屬性那麼 新的值就會覆寫這個屬性
setattr(d,'name','name change to jack')
print("原本名字是Fly改變後的名字是:\t",d.name)

# 運作結果:
# 将執行個體d添加一個新屬性sex=Male:     Male
# 原本名字是Fly改變後的名字是:      name change to jack
           

10.4、delattr删除執行個體屬性

class Dog(object):
    def __init__(self,name):
        self.name = name
    def eat(self,food):
        print("%s is eating....."%self.name)
d = Dog("NiuHanYang")
choice = input(">>:").strip()
if hasattr(d,choice):
    delattr(d,choice)  #使用delattr(d,choice)删除執行個體的屬性那麼是以下面列印就會報錯
print(d.name)

# 運作結果:
# >>:name   #輸入的值是name
# 下面是報錯資訊
# Traceback (most recent call last):
#   File "C:/Users/admin/PycharmProjects/s14/Day7/test1.py", line 10, in <module>
           

11、單例模式講解

11.1、單例模式原理及作用

  • 1、單例模式:永遠用一個對象得執行個體,避免建立太多執行個體浪費資源
  • 2、實質:使用__new__方法建立類對象時先判斷是否已經建立過,如果建過就使用已有的對象
  • 3、使用場景:如果每個對象内部封裝的值都相同就可以用單例模式

11.2、建立單例模式舉例

class Foo(object):
   instance = None
   def __init__(self):
      self.name = 'alex'

   def __new__(cls, *args, **kwargs):
      if Foo.instance:
         return Foo.instance
      else:
         Foo.instance = object.__new__(cls,*args,**kwargs)
         return Foo.instance

obj1 = Foo()       # obj1和obj2擷取的就是__new__方法傳回的内容
obj2 = Foo()
print(obj1,obj2)   # 運作結果: <__main__.Foo object at 0x00D3B450>    <__main__.Foo object at 0x00D3B450>

# 運作結果說明:
# 這可以看到我們建立的兩個Foo()對象記憶體位址相同,說明使用的•同一個類,沒有重複建立類
           

12、反射

  • hasattr:判斷目前類是否有這個方法
  • getattr:通過字元串反射出這個方法的記憶體位址
  • setattr:将目前類添加一個方法
  • delatrr:删除執行個體屬性

各位看官,此般可好!