1. 程式人生 > >(D20)Python-異常高階,運算子過載

(D20)Python-異常高階,運算子過載

異常(高階)

異常相關的語句:

  • try-except
  • try-finally
  • raise 觸發異常,發生錯誤通知
  • assert 根據條件觸發AssertionError型別的錯誤通知

with 語句

with語句

  • 語法:
    • with 表示式1[as 變數1], 表示式2 [as 變數2], …:
      • 語句塊
  • 作用:
    • 使用於對資源進行訪問的場合,確保使用過程中不管是否發生異常,都會執行必須的’清理’操作,並釋放資源
    • 如: 檔案使用後自動關閉,執行緒中鎖的自動獲取和釋放等
  • 說明:
    • 執行表示式用as子句中的變數繫結生成的物件
    • with 語句並不改變異常的的狀態

示例

src_file = input("請輸入原始檔: ")
 
try:
    src = open(src_file, 'rb')
    try:
        try:
            # 準備開啟別一個檔案
            dst_file = input("請輸入目標檔案: ")
            dst = open(dst_file, 'wb')
            try:
                # 開始讀寫檔案
                b = src.read()
                dst.write(b)
            finally:
                # 關閉檔案
                dst.close()
        except OSError:
            print('開啟寫檔案失敗')
    finally:
        src.close()
except OSError:
    print("開啟檔案", src_file, '失敗')

示例

src_file = input("請輸入原始檔: ")
dst_file = input("請輸入目標檔案: ")
 
try:
    with open(src_file, 'rb') as src:
        # 準備開啟別一個檔案
        with open(dst_file, 'wb') as dst:
            # 開始讀寫檔案
            b = src.read()
            dst.write(b)
except OSError:
    print("複製失敗")
 
src_file = input("請輸入原始檔: ")
dst_file = input("請輸入目標檔案: ")
 
try:
    with open(src_file, 'rb') as src, \
         open(dst_file, 'wb') as dst:
        b = src.read()
        dst.write(b)
except OSError:
    print("複製失敗")

環境管理器(也有叫上下文管理器)

  • 1.類內有__enter__方法和__exit__例項方法的類被稱為環境管理器
  • 2.能夠用with語句進行管理的物件必須是環境管理器
  • 3.__enter__將在進入with語句時被呼叫,並返回由 as 變數繫結的物件
  • 4.__exit__將在離開with語句時被呼叫,且可以用引數來判斷在離開with語句時是否有異常發生並做出相應的處理

示例

class A:
    '''此類的物件可以用於with語句進行管理'''
    def __enter__(self):
        print("已經進入with語句")
        return self
 
    def __exit__(self, exc_type, exc_value, exc_tb):
        print("已經離開了with語句")
        if exc_type is None:
            print("在with語句內部沒有發生異常,正常離開with")
        else:
            print("離開with語句時出現異常")
            print("異常型別是:", exc_type)
            print("錯誤的值是:", exc_value)
 
try:
    with A() as a:
        print("這是with語句裡列印的")
        3 / 0  # 觸發異常
except:
    print("有異常發生,程式已轉為正常!")
 
print("程式退出")

運算子過載

  • 什麼是運算子過載:
    • 讓自定義的類生成的物件(例項)能夠例項運算子進行操作
    • 作用:
      • 讓自定義類的例項像內建物件一樣進行運算子操作
      • 讓程式簡潔易讀
      • 對自定義的物件將運算子賦予新的運算規則
    • 說明:
      • 運算子過載方法的引數已經有固定的含義,不建議改變原有的意義

算術運算過載

方法名 運算子和表示式 說明
add(self,rhs) self + rhs 加法
sub(self,rhs) self - rhs 減法
mul(self,rhs) self * rhs 乘法
truediv(self,rhs) self / rhs 除法
floordiv(self,rhs) self // rhs 地板法
mod(self,rhs) self % rhs 求餘(取模)
pow(self,rhs) self ** rhs 冪運算

rhs (right hands side)

示例

class MyNumber:
    def __init__(self, v):
        self.data = v
 
    def __repr__(self):
        return "MyNumber(%d)" % self.data
 
    def __add__(self, other):
        '''實現加法操作,生成一個新的物件並返回給呼叫者'''
        print("__add__方法被呼叫")
        return MyNumber(self.data + other.data)
    def __sub__(self, rhs):
        return MyNumber(self.data - rhs.data)
 
n1 = MyNumber(100)
n2 = MyNumber(200)
n3 = n1 + n2  #  等同於n1.__add__(n2)
# n3 = n1.__add__(n2)
 
print(n1, "+", n2, '=', n3)  # MyNumber(300)    ???
n4 = n1 - n2
print('n4 =', n4)

二元運算子的過載方法格式:

    def __xxx__(self,other):
        運算規則的語句...

反向算術運算子的過載

  • 當運算子的左側為內建型別時,右側為自定義型別進行算術運算時,會出現TypeError錯誤,
  • 因無法修改內建型別的程式碼來實現運算子過載,此時需要使用反向運算子過載來完成過載

反向算術運算過載
方法名 運算子和表示式 說明
radd(self,rhs) rhs + self 加法
rsub(self,rhs) rhs - self 減法
rmul(self,rhs) rhs * self 乘法
rtruediv(self,rhs) rhs / self 除法
rfloordiv(self,rhs) rhs // self 地板法
rmod(self,rhs) rhs % self 求餘(取模)
rpow(self,rhs) rhs ** self 冪運算

示例

class MyList:
    def __init__(self, iterable=()):
        self.data = [x for x in iterable]
 
    def __repr__(self):
        return 'MyList(%r)' % self.data
 
    def __mul__(self, rhs):
        print("__mul__")
        return MyList(self.data * rhs)
 
    def __rmul__(self, lhs):
        print("__rmul__被呼叫")
        return MyList(self.data * lhs)
 
L1 = MyList([1, 2, 3])
L6 = 2 * L1  # 等同於2.__add__(L1) 或 L1.__rmul__(2)
print(L6)

複合賦值算術運算子過載

  • 以複合賦值算術運算子為例x += y 為例,此運算子會優先呼叫x.iadd(y) 方法,
    如果沒有__iadd__方法時會將複合賦值運算子拆為x = x + y,然後呼叫x = x.add(y) 方法
    其它複合賦值算術運算子也具有相同的規則

複合算術運算過載
方法名 運算子和表示式 說明
iadd(self,rhs) self += rhs 加法
isub(self,rhs) self -= rhs 減法
imul(self,rhs) self *= rhs 乘法
itruediv(self,rhs) self /= rhs 除法
ifloordiv(self,rhs) self //= rhs 地板法
imod(self,rhs) self %= rhs 求餘(取模)
ipow(self,rhs) self **= rhs 冪運算

比較運算子的過載
方法名 運算子和表示式 說明
lt(self,rhs) self < rhs 小於
le(self,rhs) self <= rhs 小於等於
gt(self,rhs) self > rhs 大於
ge(self,rhs) self >= rhs 大於等於
eq(self,rhs) self == rhs 等於
ne(self,rhs) self != rhs 不等於

注:比較運算子通常返回布林值True 或False

示例

class MyList:
    def __init__(self, iterable=()):
        self.data = [x for x in iterable]
 
    def __repr__(self):
        return 'MyList(%r)' % self.data
 
    def __gt__(self,rhs):
        '''只比較第一個元素'''
        print("__gt__被呼叫")
        return self.data > rhs.data
 
    def __eq__(self, rhs):
        print('__eq__被呼叫')
        return self.data == rhs.data
 
L1 = MyList([1, 2, 3])
L2 = MyList([1, 2, 3])
 
print(L1, '>', L2, '=', L1 > L2)
print(L1, '<', L2, '=', L1 < L2)
print(L1, '==', L2, 'is', L1 == L2)
print(L1, '!=', L2, 'is', L1 != L2)

位運算子的過載
方法名 運算子和表示式 說明
invert(self,rhs) ~self 取反(一元運算子)
and(self,rhs) self & rhs 位與
or(self,rhs) self | rhs 位或
xor(self,rhs) self ^ rhs 位異或
lshift(self,rhs) self << rhs 左移
rshift(self,rhs) self >> rhs 右移

反向位運算子的過載
方法名 運算子和表示式 說明
rand(self, rhs) lhs & self 位與
ror(self, rhs) lhs | self 位或
rxor(self, rhs) lhs ^ self 位異或
rlshift(self, rhs) lhs << self 左移
rrshift(self, rhs) lhs >> self 右移

複合賦值位運算子的過載
方法名 運算子和表示式 說明
iand(self, rhs) self &= rhs 位與
ior(self, rhs) self |= rhs 位或
ixor(self, rhs) self ^= rhs 位異或
ilshift(self, rhs) self <<= rhs 左移
irshift(self, rhs) self >>= rhs 右移

一元運算子的過載
方法名 運算子和表示式 說明
neg(self) - self 負號
pos(self) + self 正號
invert(self) ~ self 取反

語法格式:
  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):
        '''規則是正變負,負變正'''
        # L = [-x for x in self.data]
        L = (-x for x in self.data)
        return MyList(L)
 
L1 = MyList([1, -2, 3, -4, 5])
L2 = -L1  # 等同於L1.__neg__()
print(L2)
    in / not in 運算子

contains(self, e) e in self 成員運算

索引和切片運算子的過載
    []
  過載方法               運算子和表示式    說明
__getitem__(self, i)    x = self[i]  索引/切片取值
__setitem__(self,i,val) self.[i]=val 索引/切片賦值
__delitem__(self, i)    del self[i]  del語句索引/切片

作用:
    讓自定義的型別的物件能夠支援索引和切片操作

示例

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("i =", i)
        return self.data[i]
    def __setitem__(self, i, val):
        self.data[i] = val
 
L1 = MyList([1, -2, 3, -4, 5])
x = L1[0]  # L1.__getitem__(0)
print(x)
 
L1[1] = 2  # L1.__setitem__(1, 2)
print(L1)

slice 建構函式

  • 作用:
    • 用於建立一個slice切片物件, 此物件儲存一個切片起始值,終止值, 步長值資訊
  • 格式:
    • slice(start=None, stop=None, step=None) 建立 一個slice 切片物件
    • slice 物件屬性
    • s.start 切片的起始值
    • s.stop 切片的終止值
    • s.step 要片的步長

特性屬性 @property

  • 實現其它語言所擁有的 getter 和 setter功能

    • 作用:
      • 用來模擬一個屬性
      • 通過@property裝飾器可以對模擬的屬性賦值和取值加以控制

示例

class Student:
    def __init__(self, score):
        self.__score = score
 
    def get_score(self):
        '''實現getter'''
        return self.__score
 
    def set_score(self, s):
        '''實現setter'''
        print("正在呼叫setter")
        if 0 <= s <= 100:
            self.__score = s
        else:
            raise ValueError
 
    score = property(get_score, set_score)
 
s = Student(59)
print(s.score)  # print(s.get_score())
s.score = 97  # s.set_score(97)
print(s.score)  # ...

練習:
實現有序集合類 OrderSet 能實現兩個集合的
交集 & 全集 | 補集 - 對稱補集 ^ == / !=
in / not in 等集合操作
要求內部用list 儲存
class OrderSet:

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(“不相等”)
if s2 == OrderSet([3,4,5]):
print(‘s2 等於 OrderSet([3,4,5])’)
if 2 in s1:
print(‘2 在s1內’)