Python : Class

  Python 和 JavaScript同樣便是面向過程語言,也是面嚮對象語言,動態語言。大多數面嚮對象語言裏,Class是必不可少的。面向對象有三大特性:封裝, 繼承,多態。在Python中Class究竟是怎樣的呢?java

一、Class組成

  先來看一個示例:python

class Person(object):
    id=''
    name = ''
    age = 3
    
    # 等同於Java中的<init>,即構造器
    def __init__(self, id, name, age):
        print("init a Person instance")
        self.id = id
        self.name = name
        self.age = age

    def show(this):
        print(this)
    #   print(this.toString())

    #def toString(self):        
    #    return "id:{}, name:{}, age:{}".format(self.id, self.name, self.age)
    
    # 等同於Java中的toString
    def __str__(self):
    #   return self.toString()
        return "id:{}, name:{}, age:{}".format(self.id, self.name, self.age)
    

    # 等同於Java中的finalize方法,del 實例時調用
    def __del__(self):
        print("finally a Person instance")
        self.id = None
        self.name = None
        self.age = None
        self = None
    

 


  下面是以對比Java的方式,來講明Python中的類: 算法

  1)Class中,包括屬性、方法,它們都是public的。在Python的Class中,是不存在private,protected等修飾符的。編程

  2)__init__是構造函數,調用構造器時,會自動調用__init__。它至關於Java中的<init>。在建立一個Python對象時,不須要像Java那樣使用new。app

  3)__del__是析構函數,當del instance時,會自動調用__del__。它至關於Java中的finalize編程語言

  4)須要獲取對象的字符串表示時,會調用__str__。它就至關於Java中的toString。函數

  5)類的方法的第一個參數,都是self,至關於Java中的this。其實Java中的實例方法的第一個參數也是this,只是被隱藏了而已,若是你瞭解JVM 運行時的話,或者使用過 javassist或者asm等字節碼工具的話,會知道的。上面的示例中的p1.show()運行時,能夠理解爲執行的是Person.show(p1)。 並非說必須得寫成self,你也能夠寫完this或者其餘任何的知足變量命名的形式。但一般你們都約定俗稱的寫爲self,保持編碼風格的統一,有利於方便他人理解代碼。工具

  6)__init__,__del__,__str__不是必須的。學習

  7)全部屬性都要有初始值測試

 

二、Class getter,setter

  Java,JavaScript ES6都支持setter,getter。Python中也是支持的。

class Person(object):
    id=''
    name = ''
    age = 3
    
    # 等同於Java中的<init>,即構造器
    def __init__(self, id, name, age):
        print("init a Person instance")
        self.id = id
        self.name = name
        self.age = age

    def show(this):
        print(this)
    #    print(this.toString())

   #def toString(self):        
   #   return "id:{}, name:{}, age:{}".format(self.id, self.name, self.age)
    
    # 等同於Java中的toString
    def __str__(self):
   #   return self.toString()
        return "id:{}, name:{}, age:{}".format(self.id, self.name, self.age)
    

    # 等同於Java中的finalize方法,del 實例時調用
    def __del__(self):
        print("finally a Person instance")
        self.id = None
        self.name = None
        self.age = None
        self = None

    def __get__(self, name):
        print("invoke in __get__")
        print(self.__dict__)
        return 1111

    def __getattr__(self, name):
        print("invoke in __getattr__")
        return 1111

    def __getattribute__(self, name):
        print("invoke in __getattribute__")
        print(object.__getattribute__(self, name))
        print("after invoke in __getattribute__")
        return object.__getattribute__(self, name)

  對於舊式類,訪問屬性的順序是: 

  1)直接訪問屬性

  2)訪問__getattr__

  對於新式類,訪問屬性的順序是:

  1)__getattribute

  2)直接訪問屬性

  3)__getattr__

  切記,若是寫了__getattribute__,最後一句話必須是object.__getattribute(self,name) 不然就會出現:’XxxType’ object is not callable 。它的存在,更像是做爲攔截器使用。

三、Class繼承

  C++是多繼承的,Java是單繼承的,Python借鑑了C++的多繼承方式。

  在繼承結構下,訪問屬性,方法時,與Java中同樣的,先本身的,本身沒有時,纔去從父類找。

  因爲支持多繼承,因此當一個屬性、方法,在多個父類中都存在時,會訪問到哪一個呢?

  要解答這個問題,就得先知道搜索屬性、方法的順序。採用的搜索算法是深度優先搜索算法。

  也就是一個class A(S1,S2,S3):pass; 要調用一個屬性時,會先從A裏找,找不到再從S1,若是還找不到再從S2,依次類推。

 

  那麼在多繼承狀況下,若是A,S1,S2,S3都重寫了__getattr__方法,那會有什麼區別呢?查找順序大致不變的:

  1)  A的直接屬性,找不到而後是A.__getattr__

  2)  S1的直接屬性,找不到而後是S1.__getattr__

  3)  S2的直接屬性,找不到而後是S2.__getattr__

  4)  S3的直接屬性,找不到而後是S3.__getattr__

 

  對於構造函數__init__,在構造實例時,不會像Java那樣,先去調用父類的__init__。

class Student(Person):
    def __init__(self, id, name, age,email):
        print("invoke in Student __init__")
        super(Student, self).__init__(id, name, age)
        self.email = email

 

  Java中有super(),Python中是否有呢?可否利用super呢?若是不能自動調用父類的構造器,那麼在重寫__init__又得給全部的屬性都分配一下,這個好麻煩的,該怎麼辦呢?只能以編程的方式本身調用了。

   super(類,instance),這個函數的做用是找到指定的類的下一個父類的指定方法,將該方法在指定的實例上調用。

     例如上面的例子中,繼承關係是這樣的:Student > Person > object。self是Student對象,super(Student, self).__init__(id, name, age)該語句的意思就是:找到Student的下一個父類(即Person)的__init__方法,而後在self上調用該__init__方法。

 

  須要注意的是:

  1super函數的兩個參數,不能有誤。須要知足兩個條件,第二個instance應該是第一個參數所表明的類的實例(或者是其子類的實例)。

  2)Super只能在新式類中使用。

 

 

四、運算符重寫

  在.Net和Scala編程語言中,都是支持運算符重寫的。若是沒有接觸過這些東西,可能會費解的。其實只要把運算符看着是方法就能夠了。從這個角度來理解的話,Java也是支持運算符重寫的,只不過呢,該特性並無暴露給用戶罷了。例如字符串拼接 + ,它實際上是調用的StringBuilder.append方法,遍歷集合的foreach,它其實調用的是iterator。既然提到了運算符重寫,那麼Python中也必然是支持的,而且它把這個特性暴露出來了。

 

  Python String 就重寫了幾個運算符:

+ 字符串拼接
* copy多份
== 比較字符串內容
> 字符串比較
< 字符串比較

  那麼怎樣實現運算符重寫呢? 

  上面已經說明,將運算符看做是一個方法。那麼重寫運算符,就是就是重寫方法了。

  下面列出了常見的運算符重載:

重載方法

說明

調用

__init__

構造器

對象創建,X=Class()

__del__

析構方法

對象回收,或者del X

__add__

運算符 +

X+Y, X+=Y

__iadd__

加強的 +

X+Y, X+=Y

__radd__

左側+

Noninstance + Y

__or__

運算符 | (位 OR)

X|Y, X|=Y

__repr__,__str__

打印,轉換

print(X), repr(X),str(X)

__getattr__

點號運算符

獲取屬性

__setattr__

賦值運算符

設置屬性

__call__

函數調用

Y()

__len__

長度

len(X)

__cmp__,

__lt__,

__eq__

比較

X < Y

X==Y

__getitem__

索引運算符

X[name]

__setitem__

索引賦值

X[Y]=Z

__iter__

迭代

循環,迭代等

 

  使用__getitem__可使得對象具有索引的方式來訪問?

  測試以下:對上面的Student以下:

 

class Student(Person):
    def __init__(self, id, name, age,email):
        print("invoke in Student __init__")
        super(Student, self).__init__(id, name, age)
        self.email = email


    def __getitem__(self, name):
        return self.__dict__[name] + "_suffix"

  執行測試: 

  

import model
Student = model.Student
s = Student('0001', 'fjn', 20, 'fs1194361820@163.com')
s.show()
print(s["email"])

  發現結果是:fs1194361820@163.com_suffix 

 

五、模擬私有屬性

  經過上面的學習,瞭解到Python的屬性、方法都是public, 這點和JavaScript太 同樣了。寫Java時間久了,私有屬性的好處,怎麼能夠浪費了呢。那麼如何模擬出私有的?

有兩種方式能夠模擬的,都與JavaScript裏的理念是相似的。

  方式一:使用對象時,直接加屬性。

  方式二:利用對象的__dict__

 

六、Static Method

  在java中,會將實例方法與類方法區分,具體作法是在 class 方法上加上static修飾符。在python中是沒有static的,那麼如何實現static方法。

  前面也講了,實例方法聲明時,第一個參數是self。在python中,實例方法本質就是調用類方法的。  

p1=Person(‘a’,’b’,23)
Person.show(p1)

 

在方法前加上註解:@staticmethod

相關文章
相關標籤/搜索