目录:
异常(高级) 一元运算符重载 、 关系运算符的重载 、
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(“不相同”)
其它自己测试….