###day19回顾
封装
多态
多继承
内建函数的调用规则重写(覆盖)
实现自定义的可迭代对象

封装:

私有属性和私有方法
以__开头的标识符
多态:
不同的对象具有相同名称的方法,调用时统一规则来调用,但实质
上是调用不同类内的方法

多继承是指一个子类继承自两个或两个以上的父类(基类)
class ABC(A, B, C):
pass
ABC.__mro__ 记录方法的查找(调用)顺序

super(起点类, 对象).实例方法名()

内建函数:

repr(x)    __repr__(self)
str(x) __str__(self)
len(x) __len__(self)
abs(x) __abs__(self)
...
int(x) __int__(self)
...
bool(x) __bool__(self)

四个和对象相关的内建函数:

getattr(obj, name[,default])
hasattr(obj, name)
setattr(obj, name, value)
delattr(obj, name)

可迭代对象:

class 类名:
def __iter__(self):
return 迭代器

迭代器

class 类名:
def __next__(self):
# 此处实现迭代器协议
if 没有数据:
raise StopIteration
else:
return 数据

新的一天

异常(高级)

回顾异常相关的语句
try-except
try-finally
raise
assert
with 语句

with 语句:

1. 语法:
with 表达式1 [as 变量1], 表达式2 [as 变量2], ...:语句块
2. 作用:
使用于对资源进行访问的场合,确保使用过程中不管是否发生异常,
都会执行必要有"清理"操作,并释放资原.
如:文件使用后的自动关闭,线程中锁的自动获取和释放等(线程
后面会学)
3. 说明:
with 语句并不改变异常的状态
示例见:
```
try:
fr = open('../day20.txt')
try:
s = fr.readline()
print("第一行是:", s)
int(input("请制造错误:"))
finally:
fr.close()
print("操作成功")
except OSError:
print("打开文件失败")

```
练习:
把之前复制文件练习:
把打开文件的部分改用with语句打开

环境管理器

类内有 __enter__ 和 __exit__ 实例方法的类被称为环境管理器
能够用with进行管理的对象必须是环境管理器
__enter__将在进入with语句时被调用并返回由 as 变量管理的对象
__exit__将在离开with语句时被调用,且可以用参数来判断在离开
with语句时是否有异常发生并做出相应的处理

示例见:
```
class A:
def __enter__(self):
'''此方法必须返回由as绑定的对象'''
print("已进入with语句")
return self

def __exit__(self, e_type, e_value, e_traceback):
print("已离开with语句")
if e_type is None:
print("离开with时没有发生异常")
else:
print("离开with时有异常发生")
print("e_type:", e_type)
print("e_value:", e_value)
print("e_traceback:", e_traceback)

# a = A()
with A() as a:
x = int(input("请输入: "))
```

运算符重载

什么是运算符重载
让自定义的类生成的对象(实例)能够使用运算符进行操作
作用:
让自定义的类的实例像内建对象一样进行运算符操作
让程序简洁易读
对自定义的对象将运算符赋予新的运算规则
说明:
运算符重载的方法的参数个数已经固定,不可以自定义
不建议改变运算符原有的含义

算术运算符重载:
方法名 运算符和表达式 说明
__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) 右手边
二元运算符的重载格式:
def __xxx__(self, oter):
...
示例见:
```
class MyNumber:
def __init__(self, value):
self.data = value

def __repr__(self):
return "MyNumber(%s)" % self.data

def __add__(self, other):
v = self.data + other.data
return MyNumber(v) # 创建新对象并返回

def __sub__(self, rhs):
return MyNumber(self.data - rhs.data)

n1 = MyNumber(100)
n2 = MyNumber(200)
# n3 = n1.__add__(n2)
n3 = n1 + n2 # 等同于 n3=n1.__add__(n2)
print(n1, "+", n2, '=', n3)
n4 = n1 - n2
print(n4) # MyNumber(-100)
```

练习:
实现两个自定义列表相加
class MyList:
... 此处自己实现
L1 = MyList([1, 2, 3])
L2 = MyList([4, 5, 6])
L3 = L1 + L2
print(L3) # MyList([1, 2, 3, 4, 5, 6])
L4 = L2 + L1
print(L4) # MyList([4, 5, 6, 1, 2, 3])

反向算术运算符重载

当运算符的左侧为内建类型时,右侧为自定义类型进行算术符运算时
会出现TypeError错误
因无法修改内建类型的代码来实现运算符重载,此时需要使用反向算
 术运算符的重载来完成重载

反向算术运算符重载:

方法名                  运算符和表达式      说明
__radd__(self, lhs) lhs + self 加法
__rsub__(self, lhs) lhs - self 减法
__rmul__(self, lhs) lhs * self 乘法
__rtruediv__(self, lhs) lhs / self 除法
__rfloordiv__(self, lhs) lhs // self 地板除
__rmod__(self, lhs) lhs % self 求余
__rpow__(self, lhs) lhs ** self 幂运算
lhs (left hands side) 左手边

复合赋值算术运算符重载:

以复合赋值算术运算符 x += y 为例,此运算符会优先调
用 x.__iadd__(y) 方法,如果没有__iadd__方法时会将复合
赋值运算拆解为x = x + y, 然后调用 x = x.__add__(y)方法
,如果再不存在__add__方法,则会触发TypeError异常
其它复合赋值算术运算符也且有相同的规则

复合赋值算术运算符重载:

方法名                  运算符和表达式      说明
__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 幂运算
rhs (right hands side) 右手边

比较运算的重载

方法名                  运算符和表达式      说明
__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 不等于

位运算的重载

方法名                  运算符和表达式      说明
__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, lhs) lhs & self 位与(交集)
__ror__(self, lhs) lhs | self 位或(并集)
__rxor__(self, lhs) lhs ^ self 位异或(对称补集)
__rlshift__(self, lhs) lhs << self 左移
__rrshift__(self, lhs) 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 取反

语法:

class 类名:
def __xxx__(self):
...
示例见:
```
class MyList:
def __init__(self, iterable=()):
self.data = [x for x in iterable]

def __repr__(self):
return "MyList(%s)" % self.data

def __neg__(self):
lst = [-x for x in self.data]
return MyList(lst)

def __pos__(self):
return MyList([abs(x) for x in self.data])

L1 = MyList([1, -2, 3, -4, 5])
L2 = -L1
print("L2=", L2) # MyList([-1, 2, -3, 4, -5])

L3 = +L1
print("L3=", L3) # MyList([1, 2, 3, 4, 5])

```

in / not in 运算符重载

方法名                  运算符和表达式   说明
__contains__(self, e) e in self 成员运算

注: not in 与 in 共用__contains__ 方法,返回值取反

示例见:
```
class MyList:
def __init__(self, iterable=()):
self.data = [x for x in iterable]

def __repr__(self):
return "MyList(%s)" % self.data

def __contains__(self, e):
print('__contains__')
return e in self.data
# return True

L1 = MyList([1, -2, 3, -4, 5])
print(3 in L1) # ???
print(3 not in L1)
print(100 in L1)
print(100 not in L1)



```

索引和切片运算符重载

方法名                运算符和表达式     说明
__getitem__(self, i) x = self[i] 索引/切片取值
__setitem__(self, i, v) self[i] = v 索引/切片赋值
__delitem__(self, i) del self[i] 删除索引/切片

作用:
让自定义的类创建的对象支持索引和切片操作

示例见:
```
class MyList:
def __init__(self, iterable=()):
self.data = [x for x in iterable]

def __repr__(self):
return "MyList(%s)" % self.data

def __getitem__(self, i):
print('i=', i)
return self.data[i]

def __setitem__(self, i, v):
print('i=', i, 'v=', v)
self.data[i] = v

L1 = MyList([1, -2, 3, -4, 5])
x = L1[1] # 等同于 x = L1.__getitem__(1)
print('x=', x)
L1[1] = 999
print("L1=", L1) # MyList([1, 999, 3, -4, 5])```

slice 函数:

作用:
用于创建一个slice切片对象,此对象存储一个切片的起始
值,终止值,步长信息
格式:
slice(start=None, stop=None, step=None)

slice对象的实例属性
s.start 切片的起始值 默认为None
s.stop 切片的终止值 默认为None
s.step 切片的步长 默认为None

特性属性 @property

实现其它语言所拥有的getter 和 setter 功能

作用:
用来模拟一个属性
通过@property 装饰器可以对模拟属性的赋值和取值加以控制
示例见:
```
class Student:
def __init__(self, s=100):
self.score = s

s1 = Student(59)

scr = s1.score # 取值

print("成绩是:", scr)

s1.score = 9999999 # 赋值

print('成绩是:', s1.score)

```
```
def __init__(self, s):
self.__score = s

@property
def score(self):
print('get_score被调用!')
return self.__score

@score.setter
def score(self, s):
print("set_score 被调用!!")
assert 0 <= s <= 100, '成绩不在合法范围内!!!'
self.__score = s


s1 = Student(59)
scr = s1.score # 代替 scr = s1.get_score()
print("成绩是:", scr)
s1.score = 88
# s1.score = 99999999 # s1.set_score(9999999)
print('成绩是:', s1.score)



```

PEP8 编码规范

练习:
实现有序集合类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])
print(s1 ^ s2) # OrderSet([1,2,5])
if OrderSet([1,2,3]) != OrderSet([1,2,3,4])
print("不相等")
# 思考是否可以实现以下操作?
if 2 in s1:
print("2 在 s1 内")

```
class OrderSet:
def __init__(self, it=None):
if it is None:
self.data = []
elif it:
self.data = [x for x in it]

def __repr__(self):
return "OrderSet(%r)" % self.data

def __and__(self, rhs):
return OrderSet(
(x for x in self.data if x in rhs.data)
)

def __or__(self, rhs):
return OrderSet(
self.data + [x for x in rhs.data
if x not in self.data]
)

def __sub__(self, rhs):
return OrderSet(
(x for x in self.data if x not in rhs.data)
)

def __xor__(self, rhs):
return (self - rhs) | (rhs - self)

def __eq__(self, rhs):
return self.data == rhs.data

def __ne__(self, rhs):
return self.data != rhs.data

def __contains__(self, ele):
return ele in self.data


s0 = 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])
print(s1 ^ s2) # OrderSet([1,2,5])
if OrderSet([1, 2, 3]) != OrderSet([1, 2, 3, 4]):
print("不相等")
# 思考是否可以实现以下操作?
if 2 in s1:
print("2 在 s1 内")

if 100 not in s1:
print("100 不在 s1 内")


```