python基础(20)——异常 & 运算符重载 & 特性属性

一、异常(高级)

   with 语句
   语法:
       with 表达式1 [as 变量1], 表达式2 [as 变量2], ...:
          语句块
   作用:
       使用于对资源进行访问的场合,确保使用过程中不管是否发生异常都会执行必须的'清理'操作, 并释放资源
          如:文件打开后自动关闭,线程中锁的自动获取和释放等(线程后面会学)
   说明:
        as 子句中的变量用于绑定表达式执行后生成的对象

 1 #1.第一种方式用try-finally保证文件一定能够正常关闭 
 2 try:
 3     f = open("../../day19/day19.txt")
 4     try:
 5         for l in f:
 6             x = int('aaaa')  # 出现异常
 7             print(l)
 8     finally:
 9         f.close()
10         print("文件已经关闭")
11 except OSError:
12     print("打开文件失败")
with1.py

     with语句并不会改变异常的状态

 1 #2.第二种方式用with语句保证文件一定能够正常关闭  
 2 try:
 3     with open("../../day19/day19.txt") as f:
 4         for l in f:
 5             x = int('aaaa')  # 当进入异常流程时,打开的文件也能被关闭
 6             print(l)
 7 
 8     # f = open("../../day19/day19.txt")
 9     # try:
10     #     for l in f:
11     #         x = int('aaaa')  # 出现异常
12     #         print(l)
13     # finally:
14     #     f.close()
15     #     print("文件已经关闭")
16 except OSError:
17     print("打开文件失败")
with2.py

  环境资源管理器
     1. 类内有 __enter__和 __exit__实例方法的类创建的对象被称为环境管理器
     2. 能够用with语句进行管理的对象必须是环境管理器
     3. __enter__方法将在进入with语句时被调用, 由as变量绑定返回的对象
     4. __exit__方法将在离开with语句时被自动调用,且可以通过参数来判断离开with语句时是否有异常发生

 1 #3.自定义的对象加入__enter__ 和 __exit__ 方法,让A类的对象能够使用with使用语句  (资源管理器)
 2 class A:
 3     '''此类的对象可以用于with语句进行管理'''
 4     def __enter__(self):
 5         print("此方法是在with语句内执行的")
 6         return self  # self将 被 with 中的as 变量绑定
 7 
 8     def __exit__(self, exc_type, exc_val, exc_tb):
 9         '''exc_type 用来绑定错误类型,当没有异常发生时绑定None
10            exc_val 用来绑定错误对象,当没有发生异常时绑定None
11            exc_tb 用来绑定TraceBack对象,当没有异常时绑定None
12         '''
13         if exc_type is None:
14             print("您已离开with语句,离开时没有发生任何异常")
15         else:
16             print("您已离开with语句")
17             print("错误类型是:", exc_type)
18             print("错误对象是:", exc_val)
19             print('Traceback:', exc_tb)
20 
21 with A() as a:
22     print("这是with语句内部的输出")
23     int(input("请输入整数: "))
24 
25 print("程序正常结束")
with3.py

二、运算符重载

 1、什么是运算符重载

     让自定义的类生成的对象(实例) 能够使用运行算进行操作

 2、作用:

       1、让自定义类的实例像内建对象一样进行运算符操作
       2、让程序简洁易读
       3、对自定义对象将运算符赋予新的规则

3、算术运算符的重载:

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

  说明:
     运算符重载方法及参数已经有固定的含义,不建议改变原的运算符的含义及参数的意义

  二元运算符的重载方法格式:
     def __xxx__(self, other):
         语句块

 1 #算术运算符的重载方法 
 2 class MyNumber:
 3     def __init__(self, v):
 4         self.data = v  # self.data 用来保存对象的数据
 5 
 6     def __repr__(self):
 7         return "MyNumber(%d)" % self.data
 8 
 9     def __add__(self, other):
10         '''此方法来用制定self + other的规则'''
11         v = self.data + other.data
12         return MyNumber(v)  # 用v创建一个新的对象返回给调用者
13 
14     def __sub__(self, rhs):
15         return MyNumber(self.data - rhs.data)
16 
17 n1 = MyNumber(100)
18 n2 = MyNumber(200)
19 # n3 = n1.__add__(n2)
20 n3 = n1 + n2  # 等同于 n3 = n1.__add__(n2)
21 print(n3)  # MyNumber(300)
22 n4 = n3 - n2  # 等同于 n4 = n3.__sub__(n2)
23 print('n4 = ', n4)  # n4 =  MyNumber(100)
mynumer.py

 

练习:
   实现两个自定义列表的相加
   class MyList:
       def __init__(self, iterable=())
            self.data = list(iterable)
       .... 以下自己实现

  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])
   # 试想能否实现以下操作
   L5 = L1 * 3
   print(L5)  # MyList([1, 2, 3, 1, 2, 3, 1, 2, 3])

class MyList:
    def __init__(self, iterable=()):
        self.data = list(iterable)

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

    def __add__(self, rhs):
        return MyList(self.data + rhs.data)

    def __mul__(self, rhs):
        '''rhs 为int类型, rhs.data 是不存在的'''
        return MyList(self.data * rhs)

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])

# 试想能否实现以下操作
L5 = L1 * 3  # 等同于L5 = L1.__mul__(3)
print(L5)  # MyList([1, 2, 3, 1, 2, 3, 1, 2, 3])
练习.py

4、反向算术运算符的重载

  当运算符的左侧为内建类型时,右侧为自定义类型进行算术运算符运算时会出现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    幂运算

 1 class MyList:
 2     def __init__(self, iterable=()):
 3         self.data = list(iterable)
 4 
 5     def __repr__(self):
 6         return "MyList(%s)" % self.data
 7 
 8     def __add__(self, rhs):
 9         return MyList(self.data + rhs.data)
10 
11     def __mul__(self, rhs):
12         '''rhs 为int类型, rhs.data 是不存在的'''
13         print("__mul__ 被调用")
14         return MyList(self.data * rhs)
15 
16     def __rmul__(self, lhs):
17         print("__rmul__被调用")
18         return MyList(self.data * lhs)
19 
20 L1 = MyList([1, 2, 3])
21 L2 = MyList([4, 5, 6])
22 
23 L3 = 3 * L1
24 print(L3)
反向算术运算符的重载.py

5、复合赋值算术运算符的重载

     以复合赋值算术运算符 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   幂运算

 1 class MyList:
 2     def __init__(self, iterable=()):
 3         self.data = list(iterable)
 4 
 5     def __repr__(self):
 6         return "MyList(%s)" % self.data
 7 
 8     def __add__(self, rhs):
 9         print('__add__ 被调用')
10         return MyList(self.data + rhs.data)
11 
12     def __mul__(self, rhs):
13         '''rhs 为int类型, rhs.data 是不存在的'''
14         print("__mul__ 被调用")
15         return MyList(self.data * rhs)
16 
17     # def __iadd__(self, rhs):
18     #     print("__iadd__ 方法被调用")
19     #     self.data += rhs.data  # 请问这是在做什么?
20     #     return self
21 L1 = MyList([1, 2, 3])
22 L2 = MyList([4, 5, 6])
23 
24 print(id(L1))
25 L1 += L2  # L1 = L1 + L2  # L1.__iadd__(L2)
26 print(id(L1))
27 print(L1)
28 # L2 *= 3  # L2 = L2 * 3
29 # print(L2)
复合赋值算术运算符重载

6、比较运算符的重载

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

7、位相关运算符重载

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

8、反向位运算符重载

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

9、复合赋值位相关运算符重载

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

10、一元运算符的重载

   方法名           运算符和表达式   说明
  __neg__(self)            - self        负号
  __pos__(self)            + self        正号
  __invert__(self)         ~ self        取反

语法:
   class 类名:
       def __xxx__(self):
           ....

 1  class MyList:
 2     def __init__(self, iterable=()):
 3         self.data = list(iterable)
 4 
 5     def __repr__(self):
 6         return "MyList(%s)" % self.data
 7 
 8     def __neg__(self):
 9         G = (-x for x in self.data)
10         return MyList(G)
11 L1 = MyList([1, -2, 3, -4, 5])
12 L2 = -L1  # <<---此处会有错误
13 print(L2)  # MyList([-1, 2, -3, 4, -5])
14 # L3 = +L1 
一元运算符的重载.py

11、in / not in 运算符重载

   格式:
     def __contains__(self, e):
         语句

  注: in / not in 返回布尔值 True/False
     当重载了__contains__后, in 和 not in 运算符都可用.
     not in 运算符的返回值与 in 相反

 1 class MyList:
 2     def __init__(self, iterable=()):
 3         self.data = list(iterable)
 4 
 5     def __repr__(self):
 6         return "MyList(%s)" % self.data
 7 
 8     def __contains__(self, e):
 9         # print("+++++++++")
10         return True if e in self.data else False
11         # return e in self.data
12 
13 L1 = MyList([1, -2, 3, -4, 5])
14 
15 if 2 in L1:  # 等同于 if L1.__contains__(2):
16     print("2在L1内")
17 else:
18     print('2不在L1内')
19 
20 if 4 not in L1:  # 等同于 if not L1.__contains__(4)
21     print("4不在L1内")
22 else:
23     print("4在L1内")
in / not in 运算符的重载 .py

12、索引和切片运算符重载方法:

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

1、作用:
   让自定义的类型的对象能够支持索引和切片操作

class MyList:
    def __init__(self, iterable=()):
        self.__data = list(iterable)

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

    def __getitem__(self, i):
        '索引取值,i绑定[]内的元素'
        print('i的值是:', i)
        return self.__data[i]  # 返回data绑定列表中的第i个元素

    def __setitem__(self, i, v):
        '''此方法可以让自定义的列表支持索引赋值操作'''
        print("__setitem__被调用, i=", i, 'v=', v)
        self.__data[i] = v

    def __delitem__(self, i):
        self.__data.pop(i)  # del self.__data[i]

L1 = MyList([1, -2, 3, -4, 5])
x = L1[3]  # 能否用索引来访问自定义的MyList类型的对象呢
print(x)

L1[3] = 400  # 索引赋值
print(L1)

del L1[3]
print(L1)  # MyList([1, -2, 3, 5])
# 思考如下语句能执行吗?
print(L1[::2])  # 切片取值
索引赋值.py
 1 class MyList:
 2     def __init__(self, iterable=()):
 3         self.__data = list(iterable)
 4 
 5     def __repr__(self):
 6         return "MyList(%s)" % self.__data
 7 
 8     def __getitem__(self, i):
 9         print('i的值是:', i)
10         if type(i) is int:
11             print("用户正在用索引取值")
12         elif type(i) is slice:
13             print("用户正在用切片取值")
14             print("切片的起点是:", i.start)
15             print("切片的终点是:", i.stop)
16             print("切片的步长是:", i.step)
17         elif type(i) is str:
18             print("用户正在用字符串进行索引操作")
19             # raise KeyError
20             return "你想用字符串做什么?"
21 
22         return self.__data[i]  # 返回data绑定列表中的第i个元素
23 L1 = MyList([1, -2, 3, -4, 5])
24 print(L1[::2])  # 切片取值
25 print(L1["ABC"])
切片取值.py

2、slice构造函数

     作用:
       用于创建一个slice对象,此对于用于切片操作的传值
     格式:
         slice(start=None, stop=None, step=None)
     slice对象的实例属性:
         start  切片的起始值 默认为None
         stop   切片的终止值 默认为None
         step   切片的步长 默认为None

三、特性属性 @property

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

  作用:
     用来模拟一个属性
     通过@property装饰器,可以对模拟属性的赋值和取值加以控制

 1 class Student:
 2     def __init__(self, s):
 3         self.__score = s
 4 
 5     def setScore(self, s):
 6         '''此方法用设置值加以限制以保证数据的准确性
 7         setter是用来数据的
 8         '''
 9         if 0 <= s <= 100:
10             self.__score = s
11 
12     def getScore(self):
13         '''getter 只用来获取数据'''
14         return self.__score
15 
16 s = Student(50)
17 s.setScore(100)
18 # s.score = 100
19 # print(s.score)
20 # s.score = 10000
21 # print(s.score)
property1.py
 1 class Student:
 2     def __init__(self, s):
 3         self.__score = s
 4 
 5     @property
 6     def score(self):
 7         '''getter 只用来获取数据'''
 8         print("getter被调用")
 9         return self.__score
10 
11     @score.setter
12     def score(self, s):
13         '''此方法用设置值加以限制以保证数据的准确性
14         setter是用来数据的
15         '''
16         print("setter被调用")
17         if 0 <= s <= 100:
18             self.__score = s
19 
20 s = Student(50)
21 # s.setScore(100)
22 
23 score = s.score  # 访问特性属性score 实质是调用原s.score()
24 print('成绩是:', score)
25 s.score = 100
26 print(s.score)  # 100
27 s.score = 10000
28 print(s.score)  # 100
property2.py

练习:

   实现有序集合类OrderSet , 能实现两个集合的交集 &, 并集 |, 补集-, 对称补集 ^, ==,!=,in / not in 等操作
   要求: 集合的内部用list存储数据

  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("不相等")
  else:
       print("相等")
  if s1 == OrderSet([3, 4, 5]):
      print("s1 == OrderSet([3, 4, 5])")
   if 2 in s1:
       print('2 in s1 返回真')
   ... 以下自己测试

 1 class OrderSet:
 2     def __init__(self, it=None):
 3         if it is None:
 4             self.data = []
 5         elif it:
 6             self.data = [x for x in it]
 7 
 8     def __repr__(self):
 9         return "OrderSet(%r)" % self.data
10 
11     def __and__(self, rhs):
12         return OrderSet(
13             (x for x in self.data if x in rhs.data)
14         )
15 
16     def __or__(self, rhs):
17         return OrderSet(
18             self.data + [x for x in rhs.data
19                          if x not in self.data]
20         )
21 
22     def __sub__(self, rhs):
23         return OrderSet(
24             (x for x in self.data if x not in rhs.data)
25         )
26 
27     def __xor__(self, rhs):
28         return (self - rhs) | (rhs - self)
29 
30     def __eq__(self, rhs):
31         return self.data == rhs.data
32 
33     def __ne__(self, rhs):
34         return self.data != rhs.data
35 
36     def __contains__(self, ele):
37         return ele in self.data
38 
39 
40 s0 = OrderSet()
41 s1 = OrderSet([1, 2, 3, 4])
42 s2 = OrderSet([3, 4, 5])
43 print(s1 & s2)  # OrderSet([3,4])
44 print(s1 | s2)  # OrderSet([1,2,3,4,5])
45 print(s1 - s2)  # OrderSet([1,2])
46 print(s1 ^ s2)  # OrderSet([1,2,5])
47 if OrderSet([1, 2, 3]) != OrderSet([1, 2, 3, 4]):
48     print("不相等")
49 # 思考是否可以实现以下操作?
50 if 2 in s1:
51     print("2 在 s1 内")
52 
53 if 100 not in s1:
54     print("100 不在 s1 内")
练习

 

posted on 2018-10-12 17:41  破天荒的谎言、谈敷衍  阅读(326)  评论(0)    收藏  举报

导航