Python12月4日
面向物件高階程式設計
使用__slots__
如果我們想要限制例項的屬性怎麼辦?比如,只允許對Student例項新增name
和age
屬性。
為了達到限制的目的,Python允許在定義class的時候,定義一個特殊的__slots__
變數,來限制該class例項能新增的屬性
class Student(object):
__slots__ = ('name', 'age') # 用tuple定義允許繫結的屬性名稱
然後,我們試試:
>>> s = Student() # 建立新的例項
>>> s.name = 'Michael' # 繫結屬性'name'
>>> s.age = 25 # 繫結屬性'age'
>>> s.score = 99 # 繫結屬性'score'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'Student' object has no attribute 'score'
由於'score'
沒有被放到__slots__
中,所以不能繫結score
屬性,試圖繫結score
將得到AttributeError
的錯誤。
使用__slots__
要注意,__slots__
定義的屬性僅對當前類例項起作用,對繼承的子類是不起作用的:
>>> class GraduateStudent(Student):
... pass
...
>>> g = GraduateStudent()
>>> g.score = 9999
除非在子類中也定義__slots__
,這樣,子類例項允許定義的屬性就是自身的__slots__
加上父類的__slots__
使用@property
@property
的實現比較複雜,我們先考察如何使用。把一個getter方法變成屬性,只需要加上@property
就可以了,此時,@property
本身又建立了另一個裝飾器@score.setter
,負責把一個setter方法變成屬性賦值,於是,我們就擁有一個可控的屬性操作:
>>> s = Student()
>>> s.score = 60 # OK,實際轉化為s.set_score(60)
>>> s.score # OK,實際轉化為s.get_score()
60
>>> s.score = 9999
Traceback (most recent call last):
...
ValueError: score must between 0 ~ 100!
注意到這個神奇的@property
,我們在對例項屬性操作的時候,就知道該屬性很可能不是直接暴露的,而是通過getter和setter方法來實現的。
還可以定義只讀屬性,只定義getter方法,不定義setter方法就是一個只讀屬性:
class Student(object):
@property
def birth(self):
return self._birth
@birth.setter
def birth(self, value):
self._birth = value
@property
def age(self):
return 2015 - self._birth
上面的birth
是可讀寫屬性,而age
就是一個只讀屬性,因為age
可以根據birth
和當前時間計算出來。
多重繼承
繼承是面向物件程式設計的一個重要的方式,因為通過繼承,子類就可以擴充套件父類的功能。若需要多層的繼承,則可以通過多重繼承的方法,通過多重繼承,一個子類就可以同時獲得多個父類的所有功能。
MixIn
在設計類的繼承關係時,通常,主線都是單一繼承下來的,例如,Ostrich
繼承自Bird
。但是,如果需要“混入”額外的功能,通過多重繼承就可以實現,比如,讓Ostrich
除了繼承自Bird
外,再同時繼承Runnable
。這種設計通常稱之為MixIn。
為了更好地看出繼承關係,我們把Runnable
和Flyable
改為RunnableMixIn
和FlyableMixIn
。類似的,你還可以定義出肉食動物CarnivorousMixIn
和植食動物HerbivoresMixIn
,讓某個動物同時擁有好幾個MixIn:
class Dog(Mammal, RunnableMixIn, CarnivorousMixIn):
pass
MixIn的目的就是給一個類增加多個功能,這樣,在設計類的時候,我們優先考慮通過多重繼承來組合多個MixIn的功能,而不是設計多層次的複雜的繼承關係。
Python自帶的很多庫也使用了MixIn。舉個例子,Python自帶了TCPServer
和UDPServer
這兩類網路服務,而要同時服務多個使用者就必須使用多程序或多執行緒模型,這兩種模型由ForkingMixIn
和ThreadingMixIn
提供。通過組合,我們就可以創造出合適的服務來。
由於Python允許使用多重繼承,因此,MixIn就是一種常見的設計。
只允許單一繼承的語言(如Java)不能使用MixIn的設計。
定製類
看到類似__slots__
這種形如__xxx__
的變數或者函式名就要注意,這些在Python中是有特殊用途的。
__slots__
我們已經知道怎麼用了,__len__()
方法我們也知道是為了能讓class作用於len()
函式。
除此之外,Python的class中還有許多這樣有特殊用途的函式,可以幫助我們定製類。
__str__
使用__str__打印出來的例項不但好看,還可以很清楚的看出例項內部重要的資料
只需要定義好__str__()
方法,返回一個好看的字串就可以了:
>>> class Student(object):
... def __init__(self, name):
... self.name = name
... def __str__(self):
... return 'Student object (name: %s)' % self.name
...
>>> print(Student('Michael'))
Student object (name: Michael)
__iter__
如果一個類想被用於for ... in
迴圈,類似list或tuple那樣,就必須實現一個__iter__()
方法,該方法返回一個迭代物件,然後,Python的for迴圈就會不斷呼叫該迭代物件的__next__()
方法拿到迴圈的下一個值,直到遇到StopIteration
錯誤時退出迴圈。
我們以斐波那契數列為例,寫一個Fib類,可以作用於for迴圈:
class Fib(object):
def __init__(self):
self.a, self.b = 0, 1 # 初始化兩個計數器a,b
def __iter__(self):
return self # 例項本身就是迭代物件,故返回自己
def __next__(self):
self.a, self.b = self.b, self.a + self.b # 計算下一個值
if self.a > 100000: # 退出迴圈的條件
raise StopIteration()
return self.a # 返回下一個值
現在,試試把Fib例項作用於for迴圈:
>>> for n in Fib():
... print(n)
...
1
1
2
3
5
...
46368
75025
Python的class允許定義許多定製方法,可以讓我們非常方便地生成特定的類。
使用列舉類
Enum類的用法,定義類,繼承Eunm父類,就自動獲得了name和value
首先要先從enum模組中匯入Enum類,通過下面的語句:
from enum import Enum
type()用法:
def fn(self, name=‘world’): # 先定義函式
print(‘Hello, %s.’ % name)
Hello = type(‘Hello’, (object,), dict(hello=fn)) # 建立Hello class
class的名稱–Hello;
繼承的父類集合,注意Python支援多重繼承,如果只有一個父類,別忘了tuple的單元素寫法–object,;
class的方法名稱與函式繫結,這裡我們把函式fn繫結到方法名hello上–hello=fn。