【Python】程式設計筆記7
文章目錄
面向物件高階程式設計
高階特性:多重繼承、定製類、元類等概念。
一、slots
__slots__變數:限制該 class 例項能新增的屬性。
class Student(object):
__slots__ = ('name', 'age')
s = Student()
s.name = 'Michael'
s.age = 25
s.score = 99
print(s.name)
print(s.age)
print(s.score)
==》AttributeError 的錯誤
AttributeError: 'Student' object has no attribute 'score'
注意:__slots__
定義的屬性僅對當前類例項起作用,對繼承的子類是不起作用的。除非在子類中也定義 __slots__
,這樣,子類例項允許定義的屬性就是自身的__slots__加上父類的__slots__。
class GraduateStudent(Student):
pass
g = GraduateStudent()
g.score = 9999
print(g.score) ## 9999
二、@property裝飾器
負責把一個方法變成屬性呼叫的。
class Student(object):
@property
def score(self):
return self._score
@score.setter
def score(self, value):
if not isinstance(value, int):
raise ValueError('score must be an integer!')
if value < 0 or value > 100:
raise ValueError('score must between 0 ~ 100!')
self._score = value
s = Student()
s.score = 60
print(s.score)
s.score = 999
print(s.score)
輸出結果
60
Traceback (most recent call last):
File "E:/codes/python/basic/6.py", line 169, in <module>
s.score = 999
File "E:/codes/python/basic/6.py", line 163, in score
raise ValueError('score must between 0 ~ 100!')
ValueError: score must between 0 ~ 100!
把一個 getter 方法變成屬性,只需要加上@property 就可以了,此時, @property 本身又建立了另一個裝飾器@score.setter,負責把一個 setter 方法變成屬性賦值。
三、多重繼承——MixIn
MixIn 的目:給一個類增加多個功能。
在設計類的時候,要優先考慮通過多重繼承來組合多個 MixIn 的功能,而不是設計多層次的複雜的繼承關係。
class Animal(object):
pass
class RunnableMixIn(object):
def run(self):
print('Running...')
class Mammal(Animal):
pass
## Dog為多繼承,繼承於 Mammal 和 RunnableMixIn
class Dog(Mammal, RunnableMixIn):
pass
四、定製類——__xxx__
Python 的 class 中還有許多這樣有特殊用途的函式,可以幫
助我們定製類。
1、__str__()
與 __repr__()
class Student(object):
def __init__(self, name):
self.name = name
def __str__(self):
return 'Student object (name: %s)' % self.name
print(Student('Michael'))
結果對比:
- 未自定義
__str__()
的結果
<__main__.Student object at 0x000001994F615FD0>
- 自定義
__str__()
的結果
Student object (name: Michael)
在互動的模式下,直接輸出 s,打印出來的 <__main__.Student object at 0x109afb310>
也並不好看。
直接顯示變數呼叫的是 __repr__()
而非 __str__()
。
==》
區別:__str__()
返回使用者看到的字串,而__repr__()
返回程式開發者看到的字串,也就是說,__repr__()
是為除錯服務的。
==》解決辦法:再定義一個__repr__()
2、__iter__()
__iter__()
:用於 for…in 迴圈,該函式返回一個迭代物件。
然後,Python 的 for 迴圈就不斷用該迭代物件的 __next__()
方法獲取迴圈的下一個值,直到遇到 StopIteration 錯誤時退出迴圈。
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
for n in Fib():
print(n)
輸出結果
1
1
2
3
5
8
13
21
34
55
89
144
233
377
610
987
1597
2584
4181
6765
10946
17711
28657
46368
75025
3、__getitem__()
可以按照下標取出元素 ==》__getitem__()
方法
class Fib(object):
def __getitem__(self, n):
a, b = 1, 1
for x in range(n):
a, b = b, a + b
return a
f = Fib()
print(f[0])
print(f[1])
print(f[100])
輸出結果
1
1
573147844013817084101
==》切片操作
原因:__getitem__()
傳入的引數可能是一個 int,也可能是一個切片物件 slice,所以要做判斷。
class Fib(object):
def __getitem__(self, n):
if isinstance(n, int): # n 是索引
a, b = 1, 1
for x in range(n):
a, b = b, a + b
return a
if isinstance(n, slice): # n 是切片
start = n.start
stop = n.stop
if start is None:
start = 0
a, b = 1, 1
L = []
for x in range(stop):
if x >= start:
L.append(a)
a, b = b, a + b
return L
f = Fib()
print(f[0:5])
print(f[:10])
輸出結果
[1, 1, 2, 3, 5]
[1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
分析:沒有對 step 引數、負數等做處理。
==》將物件看出 dict
==》__getitem__()
的引數可能作 key 的 object。其對應的 __setitem__()
方法:將物件看作 list 或 dict 來對集合賦值;__delitem__()
方法:刪除某個元素。
4、__getattr__()
為了避免由於類的方法或屬性不在造成的 AttributeError 錯誤,使用 __getattr__()
,動態返回一個屬性。
class Student(object):
def __init__(self):
self.name = 'Michael'
def __getattr__(self, attr):
if attr == 'score':
return 99
elif attr == 'age':
return lambda:25
# 若均不匹配,則丟擲 AttributeError 的錯誤
raise AttributeError('\'Student\'object has no attribute \'%s\'' % attr)
==》當呼叫不存在的屬性時,比如 score, Python 直譯器會試圖呼叫__getattr__(self, ‘score’)來嘗試獲得屬性,從而可以返回score的值99。
常約定 class 只響應特定的幾個屬性,否則丟擲 AttributeError 的錯誤。
5、__call__()
對例項進行直接呼叫。
class Student(object):
def __init__(self, name):
self.name = name
def __call__(self, *args, **kwargs):
print('My name is %s' % self.name)
s = Student('Michael')
print(s())
# My name is Michael
判斷一個物件是否能被呼叫,若可被呼叫,則該物件是一個 Callable 物件——callable()
>>> callable(Student())
True
>>> callable(max)
True
>>> callable([1, 2, 3])
False
>>> callable(None)
False
>>> callable('str')
False
五、列舉類——Enum類
from enum import Enum
# Month型別的列舉類 定義
Month = Enum('Month', ('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'))
print(Month.Jun)
# 列舉所有成員,value屬性為int常量,預設從1開始計數
for name, member in Month.__members__.items():
print(name, '=>', member, ',', member.value)
精確控制列舉型別 ==》Enum的派生類
from enum import Enum, unique
@unique # 該裝飾器確保無重複值
class Weekday(Enum):
Sun = 0 # Sun 的 value 被設定為 0
Mon = 1
Tue = 2
Wed = 3
Thu = 4
Fri = 5
Sat = 6
## 列舉型別的訪問
day1 = Weekday.Mon
print(day1)
print(Weekday.Tue) # 用成員名稱引用列舉常量
print(Weekday['Tue'])
print(Weekday.Tue.value)
print(day1 == Weekday.Mon)
print(day1 == Weekday.Tue)
print(Weekday(1)) # 根據 value 的值獲得列舉常量
print(day1 == Weekday(1))
# print(Weekday(7))
for name, member in Weekday.__members__.items():
print(name, '=>', member)
既可以用成員名稱引用列舉常量,又可以直接根據 value 的值獲得列舉常量。
六、使用元類
1、type()
動態語言的函式和類的定義在執行時動態建立的。
type()函式既可以返回一個物件的型別,又可以創建出新的型別。
。。。。。
七、錯誤、除錯和測試
1、錯誤處理
處理機制:try…except…finally…(可以多個except)
如果執行出錯,則後續程式碼不會繼續執行,而是直接跳轉至錯誤處理程式碼,即 except 語句塊,執行完 except 後,如果有 finally 語句塊,則執行 finally 語句塊,至此,執行完畢。
若沒有錯誤,執行完try部分,不執行except部分而執行finally部分。
try:
print('try...')
r = 10 / int('a')
print('result: ', r)
except ValueError as e:
print('ValueError: ', e)
except ZeroDivisionError as e:
print('ZeroDivisionError: ', e)
finally:
print('finally...')
print('END')
輸出結果
try...
ValueError: invalid literal for int() with base 10: 'a'
finally...
END
Python 所有的錯誤都是從 BaseException 類派生的,常見的錯誤型別和繼承關係看這裡:
https://docs.python.org/3/library/exceptions.html#exception-hierarchy
2、呼叫堆疊
解讀錯誤資訊是定位錯誤的關鍵。我們從上往下可以看到整個錯誤的呼叫函式鏈。
3、記錄錯誤
import logging
def foo(s):
return 10 / int(s)
def bar(s):
return foo(s) * 2
def main():
try:
bar('0')
except Exception as e:
logging.exception(e)
main()
print('END')
4、丟擲錯誤
5、除錯
- print()可能錯誤的變數值
- 斷言(assert):assert 表示式, ‘輸出語句’==》表示式為真,繼續執行,否則丟擲 AssertionError錯誤並輸出後面的輸出語句。
- python -0 檔案.py ==》關閉assert
- logging輸出到檔案;
- 偵錯程式 pdb,可以單步除錯
6、單元測試
用來對一個模組、一個函式或者一個類來進行正確性檢驗的測試工作。
==》確保一個程式模組的行為符合我們設計的測試用例。
測試用例:
- 輸入正數;
- 輸入負數;
- 輸入0;
- 輸入非數值型別