python面向對象進階

1. 類的成員

python 類的成員有三種:字段、方法、屬性前端

字段

字段包括:普通字段和靜態字段,他們在定義和使用中有所區別,而最本質的區別是內存中保存的位置不一樣,python

  • 普通字段 
    屬於對象,只有對象建立以後,纔會有普通字段,並且只能經過對象來調用git

  • 靜態字段 
    屬於類,解釋器在加載代碼的時候已經建立,對象和類均可以調用程序員

  • 例子:web


  1. class Province:sql

  2.  country = '中國'           #靜態字段數據庫

  3.  def __init__(self,name):設計模式

  4.      self.name = name            #普通字段併發

  5. #調用字段:app

  6. obj = Province('河南')   #建立對象

  7. res1 = obj.name   #對象調用普通字典

  8. res2 = obj.country    #對象調用靜態字段

  9. print('對象調用普通字典:',res1)

  10. print('對象調用靜態字段:',res2)

  11. res3 = Province.country   #類調用靜態字段

  12. res4 = Province.name    #類調用普通字段,會報錯

  13. print('類調用靜態字段:',res3)

  14. print('類調用普通字段:',res4)   #報錯

  15. 輸出結果:

  16. 對象調用普通字典: 河南

  17. 對象調用靜態字段: 中國

  18. 類調用靜態字段: 中國

  19. Traceback (most recent call last):

  20. File "D:/study-file/git/gitlab/study/code/day08/成員.py", line 24, in <module>

  21. res4 = Province.name  # 類調用普通字段,會報錯

  22. AttributeError: type object 'Province' has no attribute 'name'

  23. 由於對象沒有建立,因此在內存中並無name這個字段,因此,類直接調用會報錯

總結:靜態字段在內存中只保存一份 普通字段在每一個對象中都要保存一份 應用場景: 經過類建立對象時,若是每一個對象都具備相同的字段,那麼就使用靜態字段。普通字段只能用對象訪問,靜態字段對象和類均可以訪問(優先使用類訪問)

方法

方法包括:普通方法、靜態方法和類方法,三種方法在內存中都歸屬於類,區別在於調用方式不一樣

  • 普通方法

    屬於類,由對象去調用執行,參數至少有一個self,執行普通方法時,自動將調用該方法的對象賦值給self;

  • 靜態方法

    屬於類,由類直接調用.當方法內部不須要對象中封裝的值時,能夠將方法寫成靜態,而且使用 @staticmethoe裝飾,而且參數中不帶self,參數無關緊要

  • 類方法

    靜態方法的特殊形式,至少有一個cls參數 由類執行 @classmethoe裝飾,執行類方法時,自動將調用該方法的類複製給cls

  • 舉例:


  1. class Province:

  2. country = '中國'  # 靜態字段

  3. def __init__(self, name):

  4.    self.name = name  # 普通字段

  5. def show(self):             #普通方法

  6.    print(self.country,self.name)

  7. @staticmethod

  8. def f1(arg):                    #靜態方法

  9.    print(arg)

  10. @classmethod

  11. def f2(cls):            #類方法   cls爲類名

  12.    print(cls)

  13. # 調用字段:

  14. obj = Province('河南')  # 建立對象

  15. obj.show()        #類調用普通方法執行

  16. obj.f1('對象調用靜態方法執行')

  17. Province.f1('類調用靜態方法執行')

  18. Province.f2()    #類調用類方法執行,返回類名

  19. 執行結果:

  20. 中國 河南

  21. 對象調用靜態方法執行

  22. 類調用靜態方法執行

  23. <class '__main__.Province'>

總結 相同點:對於全部的方法而言,均屬於類(非對象)中,因此,在內存中也只保存一份。不一樣點:方法調用者不一樣、調用方法時自動傳入的參數不一樣。

屬性

屬性是普通方法的變種,使用 @property來裝飾,因此具備方法的表現形式,使用字段調用的方法來調用方法,因此也具備字段的訪問形式。由對象來調用

  • 屬性的基本使用


    從執行結果中能夠看出,常規類中方法的調用是obj.方法()的形式,可是此時調用屬性是obj.方法,不加括號,這種形式和靜態字段調用的形式同樣,因此說有靜態字段的調用方法;而在代碼中看,屬性的表現形式都是普通方法的形式,即函數,而後使用property來裝飾,因此說有普通方法的表現形式

  1. class Province:

  2.    country = '中國'  # 靜態字段

  3.    def __init__(self, name):

  4.        self.name = name  # 普通字段

  5.    def show(self):             #普通方法

  6.        print(self.country,self.name)

  7.    @staticmethod

  8.    def f1(arg):                    #靜態方法

  9.        print(arg)

  10.    @classmethod

  11.    def f2(cls):            #類方法   cls爲類名

  12.        print(cls)

  13.    @property

  14.    def f3(self):  # 屬性

  15.        print(self.name)

  16.    @f3.deleter

  17.    def f3(self):

  18.        print('del  f3')

  19.    @f3.setter

  20.    def f3(self,arg):

  21.        print('set f3',arg)

  22. #調用屬性

  23. obj = Province('河南')  # 建立對象

  24. obj.f3    #調用屬性,自動執行@f3.getter裝飾的方法   此形態相似於靜態字段的調用

  25. del obj.f3   #自動執行@f3.deleter裝飾的方法,相似於靜態字段的del

  26. obj.f3 = '123'  #自動執行@f3.setter裝飾的方法,相似靜態字段的set方法

  27. 執行結果:

  28. 河南

  29. del  f3

  30. set f3 123

屬性的表現形式
  • 裝飾器:

即在一個方法上應用@property裝飾器,使方法變爲一個屬性

  ```
  class Foo:
      @property
      def f1(self):
          pass
      @f1.deleter
      def f1(self):
          pass
       @f1.setter
       def f3(self):
          pass
   ```

 * 靜態字段: 在類中定義値爲property對象的靜態字段

      ```
      class Province:
            country = '中國'  # 靜態字段

            def __init__(self, name):
                self.name = name  # 普通字段

            def show(self):             #普通方法
                print(self.country,self.name)

            @staticmethod
            def f1(arg):                    #靜態方法
                print(arg)

            @classmethod
            def f2(cls):            #類方法   cls爲類名
                print(cls)

            def f4(self):
                print(1234)

            def f5(self,arg):
                print('執行set')

            def f6(self):
                print('執行del')

            foo = property(fget=f4, fset=f5, fdel=f6)  # 屬性的靜態字段表達方式
         #調用屬性
        obj = Province('河南')  # 建立對象
        obj.foo    #自動執行f4方法
        del obj.foo  #自動執行f6方法
        obj.foo = '123'   #自動執行f5方法

        輸出結果:
        1234
        執行del
        執行set
      ```
  • 總結:

    屬性存在乎義是:訪問屬性時能夠製造出和訪問字段徹底相同的假象,按字段的操做來執行對象類中定義的屬性中特定的方法,如執行obj.foo會自動執行f4方法,del obj.foo 會自動執行f6方法,此映射關係都使用foo = property(fget=f4, fset=f5, fdel=f6)定義好,屬性只是僞造了字段的操做方式而已,不會刪除對應的東西,只是根據字段的操做方式來執行對應的方法,而具體執行什麼方法,方法有什麼功能,這都是本身靈活定義 
    屬性由方法變種而來,若是Python中沒有屬性,方法徹底能夠代替其功能。

2. 類的成員修飾符

類的成員修飾符使用類的全部成員,包括以下:

  • 公有:在任何地方都能訪問和調用

  • 私有:只能在類內部進行調用

    「` 
    class Foo: 
    contry = ‘china’ #公有靜態字段 
    __contry1 = ‘china’ #私有靜態字段

    def __init__(self,name):
        self.name = name    #公有普通字段
        self.__name1 = name   #私有普通字段
    
    def __f1(self):         #私有方法
        print(self.name)
    
    def f2(self):           #公有方法
        print(self.__contry)
        self.__f1()

    「`

    • 特例 
      若是想要強制訪問私有字段,能夠經過 對象._類名__ 私有成員名訪問 
      如:obj._Foo__\f1, obj_Foo__contry1, 不建議強制訪問私有成員

    • 定義:私有成員命名時,前兩個字符是下劃線。(特殊成員除外,例如:initcalldict 等)

3. 類的特殊成員

python的特殊成員是採用__方法名__ 表示含有特殊意義的成員

  • init 構造方法,該方法在對象建立時自動建立


  1. class Foo:

  2.  def __init__(self,name):

  3.      self.name = name    #公有普通字段

del 析構方法。

當對象在內存中被釋放時,自動觸發執行,此方法通常無須定義,由於Python是一門高級語言,程序員在使用時無需關心內存的分配和釋放,由於此工做都是交給Python解釋器來執行,因此,析構函數的調用是由解釋器在進行垃圾回收時自動觸發執行

  • doc 表示類的描述信息

  class Foo:    """ 描述類信息,牛逼的python """    def func(self):        passprint(Foo.__doc__) #輸出:類的描述信息輸出結果:    描述類信息,牛逼的python
  • module 和 class

module 表示當前操做的對象在那個模塊

class 表示當前操做的對象的類是什麼

  class Foo:    def f1(self):        pass
  from test import Foo  obj = Foo()  print(obj.__class__)  print(obj.__module__)  輸出:  <class 'test.Foo'>  test
  • call

對象後面加括號,觸發執行。注:構造方法的執行是由建立對象觸發的,即:對象 = 類名() ;而對於 call 方法的執行是由對象後加括號觸發的,即:對象() 或者 類()()

  class Foo:      def __init__(self):          pass      def __call__(self, *args, **kwargs):          print('__call__')  obj = Foo() # 執行 __init__  obj()       # 執行 __call__
  • dict 類或對象中的全部成員

 class Foo:    def __init__(self):        self.name = 123    def f1(self):        passprint(Foo.__dict__)   #打印類的全部成員obj = Foo()print(obj.__dict__)     #打印對象中的全部成員輸出結果:{'__init__': <function Foo.__init__ at 0x01FA1348>, '__module__': '__main__', '__weakref__': <attribute '__weakref__' of 'Foo' objects>, '__dict__': <attribute '__dict__' of 'Foo' objects>, '__doc__': None, 'f1': <function Foo.f1 at 0x01FA11E0>}{'name': 123}
  • str 指定print對象的時候輸出的內容

class Foo:    def __init__(self):        self.name = 123    def f1(self):        pass    def __str__(self):        return  "打印對象輸出結果"    obj = Foo()print(obj)     #打印對象輸出結果:打印對象輸出結果
  • getitemsetitemdelitem

用於索引操做,如字典。以上分別表示獲取、設置、刪除數據

 class Foo:   def __init__(self):       self.name = 123   def __getitem__(self, item):       print('__getitem__',item)   def __delitem__(self, key):       print('__delitem',key)   def __setitem__(self, key, value):       print('__setitem__',key,value)obj = Foo()result = obj['k1']      # 自動觸發執行 __getitem__obj['k2'] = 'hahhahhhha'   # 自動觸發執行 __setitem__del obj['k1']               ## 自動觸發執行 __delitem__輸出結果:__getitem__ k1__setitem__ k2 hahhahhhha__delitem k1
  • iter 用於迭代器,之因此列表、字典、元組能夠進行for循環,是由於類型內部定義了 iter

class Foo:   def __init__(self,num):       self.num = num   def __iter__(self):       i = 1       while True:           if i <= self.num:               yield i               i += 1           else:               break  obj = Foo(4)  for i in obj:      print(i)  輸出結果:  1  2  3  4

以上是經常使用的特殊成員,還有不少不經常使用的,不在舉例

4. 面向對象其餘

  • isinstance(obj, cls)

    檢查是否obj是不是類 cls 的對象

class Foo(object):    passobj = Foo()isinstance(obj, Foo)
  • issubclass(sub, super)

檢查sub類是不是 super 類的派生類

class Foo(object):    passclass Bar(Foo):    passissubclass(Bar, Foo)
  • 執行父類的方法

默認狀況下當子類和父類的方法同樣時,優先執行子類的方法,以下:

class Foo:  def f1(self):      print('Foo.f1')class Bar(Foo):  def f1(self):      print('Bar.f1')obj = Bar()obj.f1()輸出結果:Bar.f1

若是想要強制執行父類的方法呢?可使用super(子類名,self).父類方法 格式以下:

class Foo:    def f1(self):        print('Foo.f1')class Bar(Foo):    def f1(self):        super(Bar,self).f1()           #使用super 來強制執行父類的f1方法        print('Bar.f1')obj = Bar()obj.f1()輸出結果:Foo.f1        #執行父類f1的結果Bar.f1
  • 應用1,擴展原來代碼的功能

    需求:一個開源的web框架,在保證不改變源碼的狀況下,個性定製本身的環境,適應需求。這就用到了類的繼承,我新擴展的功能是在原來功能的基礎上進行擴展的,因此,我只須要將新功能類繼承源代碼的相關功能類,而後使用super強制執行父類的方法,實現基本功能,最後在新類中擴展基本功能便可。此區別於裝飾器,使用裝飾器須要在原來的類上應用裝飾器,那就改變了源碼


    若是我要擴展該功能,須要在每次f1執行前打印一個start,執行結束以後,打印一個end,看下面代碼

    from test import Foo #從源代碼中導入Foo類


    前端調用的時候,我直接調用本身建立的類便可,這就實現了基本的擴展,也不改變源代碼


    字典key 的排序是無序的,若是要實現一個有序字典,能夠根據類的繼承來本身寫一個有序字典類 
    實現思路: 
    1.繼承dict類,使新定義的類有dict的全部方法 
    2.定一個列表,用來存放字典中的key,輸出的時候循環這個列表,那麼這個字典就變成有序輸出 
    3.使用__setitem__特殊方法實現能夠自定義key value 
    4.使用__str__特殊方法實現print字典

    代碼以下:


    下面來測試


    • 應用2 實現有序字典

  1. obj = Mydict()              #建立一個字典

  2. obj['k1'] = 'v1'            #字典key value賦值

  3. obj['k2'] = 'v2'

  4. print(obj)              #打印字典

  5. print(type(obj))        #打印類型

  6. 輸出:

  7. {'k1':v1,'k2':v2}

  8. <class '__main__.Mydict'>

  1. class Mydict(dict):

  2. def __init__(self):

  3.    self.li = []

  4.    super(Mydict,self).__init__()

  5. def __setitem__(self, key, value):              #獲取obj['k1'] = 'v1'形式的賦值

  6.    self.li.append(key)                   #將key存入列表

  7.    super(Mydict, self).__setitem__(key,value)      #強制執行父類的__setitem__,實現字典功能

  8. def __str__(self):  

  9.    temp  = []

  10.    for key in self.li:             #循環列表中的key

  11.        value = self.get(key)

  12.        temp.append("'%s':%s" % (key,value))  #將key value 組成元組存入一個臨時列表

  13.    ret = "{" + ','.join(temp) + '}'    #join 來替換key value中間的空格爲冒號:,並拼接成字典形式

  14.    return ret

  1. obj = New()

  2. obj.f1()

  3. 輸出效果:

  4. ===start====

  5. 源代碼

  6. 基本功能執行完畢

  7. ===end===

  1. class New(Foo):

  2.    def f1(self):

  3.        print('===start====')

  4.        super(New,self).f1()

  5.        print('===end===')

  1. #這是源代碼類,實現打印輸出

  2. class Foo:

  3. def f1(self):

  4.    print('源代碼')

  5.    print('基本功能執行完畢')

5. 設計模式-單例模式

單例模式指的是是多個對象建立時,若是每次都須要建立一個實例,在經過該實例去執行指定的方法,這樣每次頻繁的建立實例,對內存的讀寫消耗很大,若是將他們共同的實例,經過一種判斷機制,若是實例不存在,則建立實例,而後調用某個方法;若是實例存在,則直接調用某個方法,那麼在內存中就僅僅保留了一份實例,這樣豈不更好

看下面實例,若是class Mysql 是一個數據庫鏈接池

  ```
  class Mysql:
      def __init__(self):
          self.host = 127.0.0.1
          self.port = 3306
          self.dbname = test
          self.user = jeck
          self.passwd = 123123
      def create(self):
          #執行create語句
          pass
      def delete(self):
          #執行delete語句
          pass
  ```

若是用戶須要操做數據庫,那麼須要進行下面操做 
user1 = Mysql() 
user1.create()

user2 = Mysql() 
user2.delete() 
….

發現,每來一個用戶,都須要建立一個地址池實例,而後執行某個方法,這樣在高併發的網站,直接就崩潰了 
換種思路,若是,我只建立一個地址池對象,用戶請求來以後,先進行判斷,沒有實例的話,就建立,有的話就直接使用,豈不更高效。

「`

class Mysql: 
instance = False 
def init(self): 
self.host = ‘127.0.0.1’ 
self.port = 3306 
self.dbname = ‘test’ 
self.user = ‘jeck’ 
self.passwd = ‘123123’

  def create(self):
      # 執行create語句
      pass

  def delete(self):
      # 執行delete語句
      pass
  @classmethod
  def get_instance(cls):
      if cls.instance:                 #判斷instence 是否有値,若是有的話,直接返回
          return cls.instance
      else:
          obj = cls()              #instence沒有値的話,建立對象,並將對象賦給instence
          cls.instance = obj
          return obj

obj1 = Mysql() #多例模式 
obj2 = Mysql() #多例模式

obj3 = Mysql.get_instance() #單例模式 
obj4 = Mysql.get_instance() #單例模式

#打印內存地址 
print(‘多例模式:’,obj1) 
print(‘多例模式:’,obj2) 
print(‘單例模式:’,obj3) 
print(‘單例模式:’,obj4)

輸出結果: 
多例模式: <main.Mysql object at 0x013AAC70> 
多例模式: <main.Mysql object at 0x013AACD0> 
單例模式: <main.Mysql object at 0x013AAD30> 
單例模式: <main.Mysql object at 0x013AAD30> 
「`

發現使用單例模式後,第二次建立的對象和第一次建立的對象內存地址是同樣的,即便再有成千上萬後實例,其都是公用的一個鏈接池 
總結:單利模式存在的目的是保證當前內存中僅存在單個實例,避免內存浪費!

同步更新地址:http://www.cnblogs.com/pycode

相關文章
相關標籤/搜索