1. 程式人生 > >python day18 異常(高階) 一元運算子過載 、 關係運算符的過載 、

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("程式結束")

環境管理器:

  1. 類內有__enter____exit__方法的類被稱為環境管理器
  2. 能夠用with進行管理的物件必須是環境管理器
  3. __enter__ 將在進入with語句時被呼叫,並返回由as變數管 理的物件
  4. __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中")

索引和切片運算子的過載:

過載方法:

  1. 方法

    方法名 作用
    getitem(self, i) 用於索引/切片取值
    setitem(self, i) 用於索引/切片賦值
    delitem(self, i) 用於del語句刪除索引操作
  2. 作用:
    讓自定義的型別的物件能夠支援索引和切片操作
  3. 示例見:

    
    
    # 此示例示意索引 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)
  4. 練習:
    實現有序集合類 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(“不相同”)
    其它自己測試….