python day18 異常(高階) 一元運算子過載 、 關係運算符的過載 、
目錄:
異常(高階) 一元運算子過載 、 關係運算符的過載 、
with語句
語法:
with 表示式1 [as 變數名1], 表示式2 [as 變數名2], ...
作用:
用於對資源訪問的場合,確保使用過程中不管是否發生異常,都會執行必要有”清理”操作,並釋放資源.
如:
檔案開啟後自動關閉,執行緒中鎖的自動獲取和釋放(執行緒後面會講)
說明:
with語句與try-finally相似,並不會必變異常狀態
as 子句用於繫結表示式建立的物件
示例見:
本示例示意with語句的使用方法
# 開啟檔案讀取檔案資料(try-finally來實現關閉檔案)
# def read_file():
# try:
# f = open("abcd.txt")
# try:
# while True:
# s = f.readline()
# if not s:
# break # return
# int(input("請輸入任意數字列印下一行:"))
# print(s)
# finally:
# print("檔案已經關閉")
# f.close()
# except IOError:
# print("出現異常已經捕獲!")
# except ValueError:
# print("程式已轉為正常狀態")
# 開啟檔案讀取檔案資料(with來實現關閉檔案)
def read_file():
try:
# f = open("abcd.txt")
with open('abcd.txt') as f:
while True:
s = f.readline()
if not s:
break # return
int(input("請輸入任意數字列印下一行:"))
print(s)
print("檔案已經關閉")
except IOError:
print("出現異常已經捕獲!")
except ValueError:
print("程式已轉為正常狀態")
read_file()
print("程式結束")
環境管理器:
- 類內有
__enter__
和__exit__
方法的類被稱為環境管理器 - 能夠用with進行管理的物件必須是環境管理器
__enter__
將在進入with語句時被呼叫,並返回由as變數管 理的物件__exit__
將在離開with語句時被呼叫,且可以用引數來判斷離開with語句時是否出現異常並做出相應的處理
示例:
# 本程式示意自定義的類作為環境管理器使用
class FileWriter:
def __init__(self, filename):
self.filename = filename # 此屬性用於記住檔名
def writeline(self, s):
'''此方法用於向檔案內寫入字串,同時自動新增換行'''
self.file.write(s)
self.file.write('\n')
def __enter__(self):
'''此方法用於實現環境管理器'''
self.file = open(self.filename, 'w')
print("已進入__enter__方法,檔案開啟成功")
return self # 返回值向用於 with中的as 繫結
def __exit__(self, exec_type, exec_value, exec_tb):
'''
exec_type 為異常類異,沒有異常發生時為None
exec_value 為錯誤的物件,沒有異常時為None
exec_tb 為錯誤的traceback物件
'''
self.file.close()
print("檔案", self.filename, "已經關閉")
if exec_type is None:
print("退出with時沒有發生異常")
else:
print("退出with時,有異常,型別是", exec_type,
"錯誤是", exec_value)
print("__exit__法被呼叫,已離開with語句")
try:
with FileWriter('log.txt') as fw:
while True:
s = input("請輸入一行: ")
if s == 'exit':
break
if s == 'error':
raise ValueError("故意製造的值錯誤")
fw.writeline(s)
except:
print("有錯誤發生,已轉為正常")
print("這是with語句之外,也是程式的最後一條語句")
練習:
實現檔案的複製(建議使用二進位制方式進行操作)
$ python3 mycp.py
請輸入原始檔: /etc/passwd
請輸入目標檔案: ./mypass.txt
提示: ‘檔案複製成功’ 或 ‘檔案複製失敗’
(建議使用with語句開啟檔案)
運算子過載:
什麼是運算子過載
讓自定義的類生成的物件(例項)能夠使用運算子進行操作
作用:
讓例項象數學表示式一樣進行運算操作
讓程式簡潔易讀
說明:
運算子過載方法的引數已經有固定的含義,不建議改變原有的含義
各種運算過載
算術運算子:
方法名 | 運算子 |
---|---|
add | 加法 + |
sub | 減法 - |
mul | 乘法 * |
truediv | 除法 / |
floordiv | 地板除 // |
mod | 取模(求餘) % |
pow | 冪 ** |
二元運算子過載方法格式:
def __xxx__(self, other):
....
示例:
# 此程式示意運算子過載
class MyNumber:
def __init__(self, v):
self.data = v
def __repr__(self):
return "MyNumber(%d)" % self.data
def __add__(self, other):
print("__add__方法被呼叫")
obj = MyNumber(self.data + other.data)
return obj
def __sub__(self, other):
return MyNumber(self.data - other.data)
n1 = MyNumber(100)
n2 = MyNumber(200)
# n3 = n1.__add__(n2)
n3 = n1 + n2 # 等同於 n1.__add__(n2)
print(n3)
n4 = n2 - n1
print('n4 =', n4) # MyNumber(100)
練習:
實現兩個自定義表的相加
class MyList:
def __init__(self, iterable):
self.data = [x for x in iterable]
... # 類內以下的部分自己實現
L1 = MyList([1,2,3])
L2 = MyList(range(4, 7))
L3 = L1 + L2
print("L3 =", L3) # MyList([1,2,3,4,5,6])
L4 = L1 * 2 # 實現乘法運算
print('L4 =', L4) # MyList([1,2,3,1,2,3])
反向算術運算子:
方法名 | 運算子 |
---|---|
radd | 加法 + |
rsub | 減法 - |
rmul | 乘法 * |
rtruediv | 除法 / |
rfloordiv | 地板除 // |
rmod | 取模(求餘) % |
rpow | 冪 ** |
示例見:
# 此示例示意返向算術運算子的過載
class MyList:
def __init__(self, iterable):
self.data = [x for x in iterable]
def __repr__(self):
return 'MyList(%r)' % self.data
# def __add__(self, rhs):
# return MyList(self.data + rhs.data)
def __mul__(self, rhs):
return MyList(self.data * rhs)
def __rmul__(self, lhs):
print("__rmul__被呼叫, lhs=", lhs)
return MyList(self.data * lhs) # lhs (left hand side)
L1 = MyList([1, 2, 3])
L2 = MyList(range(4, 7))
L4 = L1 * 2 # 實現乘法運算
print('L4 =', L4) # MyList([1,2,3,1,2,3])
L5 = 2 * L1 # 可以嗎?
print(L5)
複合賦值運算子過載:
方法名 | 運算子 |
---|---|
iadd | 加法 += |
isub | 減法 -= |
imul | 乘法 *= |
itruediv | 除法 /= |
ifloordiv | 地板除 //= |
imod | 取模(求餘) %= |
ipow | 冪 **= |
示例見:
# 此示例示意複合賦值算術運算子的過載
class MyList:
def __init__(self, iterable):
self.data = [x for x in iterable]
def __repr__(self):
return 'MyList(%r)' % self.data
def __add__(self, rhs):
print("__add__方法被呼叫")
return MyList(self.data + rhs.data)
def __iadd__(self, rhs):
print("__iadd__方法被呼叫")
self.data.extend(rhs.data)
return self
L1 = MyList([1, 2, 3])
L2 = MyList(range(4, 7))
print("id(L1) =", id(L1))
L1 += L2 # 相當於 L1 = L1 + L2
print('L1 =', L1)
print("id(L1) =", id(L1))
問題:
# 演算法1
a = [100]
def test(x):
x = x + x
print(x)
test(a)
print(a)
# 解法2
a = [100]
def test(x):
x += x # 此處與上題不同。結果也會不同
print(x)
test(a)
print(a)
比較的運算子的過載
方法名 | 運算子 |
---|---|
lt | < 小於 |
le | <= 小於等於 |
gt | > 大於 |
ge | >= 大於等於 |
eq | == 等於 |
ne | != 不等於 |
注:
比較運算子通常返回True或False
位運算子過載
方法名 | 運算子 |
---|---|
inert | ~ 取反(一元運算子) |
and | & 位與(交集) |
or | | 位或(並集) |
xor | ^ 位異或(對稱補集) |
lshift | << 左移 |
rshift | >> 右移 |
反向位運算子過載:
方法名 | 運算子 |
---|---|
rand | & 位與(交集) |
ror | | 位或(並集) |
rxor | ^ 位異或(對稱補集) |
rlshift | << 左移 |
rrshift | >> 右移 |
複合賦值運算子過載:
方法名 | 運算子 |
---|---|
iand | &= 位與(交集) |
ior | |= 位或(並集) |
ixor | ^= 位異或(對稱補集) |
ilshift | <<= 左移 |
irshift | `>>= 右移 |
一元運算子的過載:
方法名 | 運算子 |
---|---|
neg |
|
pos |
|
invert | ~ 按位取反 |
格式:
def __xxx__(self):
....
示例見:
# 此示例示意一元運算子的過載
class MyList:
def __init__(self, iterable):
self.data = [x for x in iterable]
def __repr__(self):
return 'MyList(%r)' % self.data
def __neg__(self):
print("__neg__方法被呼叫!")
L = (-x for x in self.data)
return MyList(L)
L1 = MyList([1, -2, 3, -4, 5])
print("L1 =", L1)
L2 = -L1
print("L2 =", L2)
in / not in運算子的過載
格式:
def __contains__(self, e): # e代表元素
...
說明:
not in 相當於 in取反,所有隻需要過載in 即可
示例見:
# 此示例示意in / not in 運算子的過載
class MyList:
def __init__(self, iterable):
self.data = [x for x in iterable]
def __repr__(self):
return 'MyList(%r)' % self.data
def __contains__(self, e): # e 代表測試元素
print("__contains__被呼叫")
for x in self.data:
if e == x: # 如果相同,則說明e在列表中
return True
return False
L1 = MyList([1, -2, 3, -4, 5])
if 2 in L1: # 需要過載 __contains__方法
print("2在L1中")
else:
print("2 不在L1中")
索引和切片運算子的過載:
過載方法:
方法
方法名 作用 getitem(self, i) 用於索引/切片取值 setitem(self, i) 用於索引/切片賦值 delitem(self, i) 用於del語句刪除索引操作 - 作用:
讓自定義的型別的物件能夠支援索引和切片操作 示例見:
# 此示例示意索引 index 和切片 slice 運算子的過載 class MyList: def __init__(self, iterable): self.data = [x for x in iterable] def __repr__(self): return 'MyList(%r)' % self.data def __getitem__(self, i): print("__getitem__被呼叫", i) return self.data[i] def __setitem__(self, i, v): self.data[i] = v L1 = MyList([1, -2, 3, -4, 5]) print(L1[2]) # 3? L1[1] = 2 print(L1)
練習:
實現有序集合類 OrderSet(), 能實現兩個集合的交集 &, 並集 |
補集 -, 對稱補集 ^, ==, != 等操作(寫集合相同)
要求:
集合內部用list儲存
class OrderSet:
def init(self, iterable):
self.data = [x for x in iterable]
…
測試用例:
s1 = OrderSet([1,2,3,4])
s2 = OrderSet([3,4,5])
print(s1 & s2) # OrderSet([3,4])
print(s1 | s2) # OrderSet([1,2,3,4,5])
print(s1 ^ s2) # OrderSet([1,2,5])
if OrderSet([1,2,3]) != OrderSet([1,2,3,4]):
print(“不相同”)
其它自己測試….