1. 程式人生 > >python 面向物件程式設計:類和例項

python 面向物件程式設計:類和例項

深度學習在構建網路模型時,看到用類來構建一個模型例項,清晰明瞭,所以這篇博文主要學習一下python類

類和例項:

類可以起到模板的作用,因此,可以在建立例項的時候,把一些我們認為必須繫結的屬性強制填寫進去。通過定義一個特殊的__init__(注意:特殊方法“__init__”前後分別有兩個下劃線!!!),方法,在建立例項的時候,就把namescore等屬性綁上去:

class Student(object):

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

__init__方法的第一個引數永遠是self,表示建立的例項本身,因此,在__init__方法內部,就可以把各種屬性繫結到self,因為self就指向建立的例項本身。有了__init__方法,建立例項的時候必須傳入與__init__方法匹配的引數,self不需要傳,Python直譯器自己會把例項變數傳進去:

>>> bart = Student('Bart Simpson', 59)
>>> bart.name
'Bart Simpson'

 和普通函式想比,類中定義的函式第一個引數永遠是例項變數self,其他沒有什麼不同。

訪問限制:

但是,從前面Student類的定義來看,外部程式碼還是可以自由地修改一個例項的namescore屬性:

>>> bart = Student('Bart Simpson', 59)
>>> bart.score
59
>>> bart.score = 99
>>> bart.score
99

 如果在屬性的名稱前加上兩個下劃線__,在python中就是一個私有變數,只有內部可以訪問,外部不能訪問,需要注意的是,在Python中,變數名類似__xxx__的,也就是以雙下劃線開頭,並且以雙下劃線結尾的,是特殊變數,特殊變數是可以直接訪問的,不是private變數,所以,不能用__name__

__score__這樣的變數名。

有些時候,你會看到以一個下劃線開頭的例項變數名,比如_name,這樣的例項變數外部是可以訪問的,但是,按照約定俗成的規定,當你看到這樣的變數時,意思就是,“雖然我可以被訪問,但是,請把我視為私有變數,不要隨意訪問”。

雙下劃線開頭的例項變數是不是一定不能從外部訪問呢?其實也不是。不能直接訪問__name是因為Python直譯器對外把__name變數改成了_Student__name,所以,仍然可以通過_Student__name來訪問__name變數:

class Student(object):

    def __init__(self, name, score):
        self.__name = name
        self.__score = score

    def print_score(self):
        print('%s: %s' % (self.__name, self.__score))

>>> bart = Student('Bart Simpson', 59)
>>> bart.__name
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'Student' object has no attribute '__name'


>>> bart._Student__name
'Bart Simpson'

但是強烈建議你不要這麼幹,因為不同版本的Python直譯器可能會把__name改成不同的變數名。

總的來說就是,Python本身沒有任何機制阻止你幹壞事,一切全靠自覺。

最後注意下面的這種錯誤寫法,表面上看,外部程式碼“成功”地設定了__name變數,但實際上這個__name變數和class內部的__name變數不是一個變數!內部的__name變數已經被Python直譯器自動改成了_Student__name,而外部程式碼給bart新增了一個__name變數: 

>>> bart = Student('Bart Simpson', 59)
>>> bart.get_name()
'Bart Simpson'
>>> bart.__name = 'New Name' # 設定__name變數!
>>> bart.__name
'New Name'

但是如果外部程式碼要獲取name和score怎麼辦?可以給Student類增加get_nameget_score這樣的方法; 如果又要允許外部程式碼修改score怎麼辦?可以再給Student類增加set_score方法:

class Student(object):
    ...

    def get_name(self):
        return self.__name

    def get_score(self):
        return self.__score


    def set_score(self, score):
        self.__score = score

 資料封裝:

面向物件程式設計的一個重要特點就是資料封裝。在上面的Student類中,每個例項就擁有各自的namescore這些資料。我們可以通過函式來訪問這些資料,比如列印一個學生的成績:

>>> def print_score(std):
...     print('%s: %s' % (std.name, std.score))
...
>>> print_score(bart)
Bart Simpson: 59

 但是,既然Student例項本身就擁有這些資料,要訪問這些資料,就沒有必要從外面的函式去訪問,可以直接在Student類的內部定義訪問資料的函式(如在上一部分中講到的獲取與修改資料的方法),我們稱之為類的方法,定義一個方法,除了第一個引數是self外,其他和普通函式一樣:

class Student(object):

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

    def print_score(self):
        print('%s: %s' % (self.name, self.score))

例項屬性和類屬性 

由於python是動態語言,由於Python是動態語言,根據類建立的例項可以任意繫結屬性。

給例項繫結屬性的方法是通過例項變數,或者通過self變數:

class Student(object):
    def __init__(self, name):
        self.name = name

s = Student('Bob')
s.score = 90

 但是,如果Student類本身需要繫結一個屬性呢?可以直接在class中定義屬性,這種屬性是類屬性,歸Student類所有:

class Student(object):
    name = 'Student'

 當我們定義了一個類屬性後,這個屬性雖然歸類所有,但類的所有例項都可以訪問到。來測試一下:

>>> class Student(object):
...     name = 'Student'
...
>>> s = Student() # 建立例項s
>>> print(s.name) # 列印name屬性,因為例項並沒有name屬性,所以會繼續查詢class的name屬性
Student
>>> print(Student.name) # 列印類的name屬性
Student
>>> s.name = 'Michael' # 給例項繫結name屬性
>>> print(s.name) # 由於例項屬性優先順序比類屬性高,因此,它會遮蔽掉類的name屬性
Michael
>>> print(Student.name) # 但是類屬性並未消失,用Student.name仍然可以訪問
Student
>>> del s.name # 如果刪除例項的name屬性
>>> print(s.name) # 再次呼叫s.name,由於例項的name屬性沒有找到,類的name屬性就顯示出來了
Student

 從上面的例子可以看出,在編寫程式的時候,千萬不要對例項屬性和類屬性使用相同的名字,因為相同名稱的例項屬性將遮蔽掉類屬性,但是當你刪除例項屬性後,再使用相同的名稱,訪問到的將是類屬性。