DAY 25多态、反射、异常处理

时间:2021-08-22 18:39:18

一.面向对象与面向过程分析

  1.print(len('123')) # 面向过程

   #清晰知道操作的功能,但不能明确操作的具体对象,需要传入参数.

  2.print('123'.__len__()) # 面向对象

   #清晰知道操作的对象,但不能确定具体的操作方法,需要.语法选择

二.接口思想

  接口:建立关联的桥梁,方便管理代码

  接口类: 用来定义功能的类,为继承它的子类提供功能

       该类的功能方法一般不需要有实现体

      实现体由继承它的子类自己去实现

  # 提供所有宠物应该有的功能

  class PetInterface:

    def close_master(self):pass

  # 提供所有看门应该有的功能

  class WatchInterface:

    def watch_door(self):pass

  # 没有继承PetInterface,WatchInterface的Dog就是普通Dog类

  # 但继承了PetInterface,该Dog就可以作为宠物狗,同理,继承WatchInterface就可以作为看门狗

  class Dog(PetInterface,WatchInterface):

    def jiao(self):pass

    def chi(self):pass

    def pao(self):pass

    # 在类中一定要重写接口的方法

    def close_master(self): # 写功能体

    def watch_door(self): # 写功能体

三.抽象父类

  抽象父类:拥有抽象方法(子类共有的方法,但父类不能有具体的实现体)的父类

  抽象方法:方法名是具体的,但实现体是抽象的(需要在子类中重写来具象化)

  注意: 有抽象方法的父类不能被实例化(假设能被实例化,就可以调用自己的抽象方法,没有任何意义)

  # 实现抽象父类的语法(abc)

  import abc # abstract base class

  class Sup(metaclass=abc.ABCMeta):

    #抽象父类中的抽象方法,在继承它的子类中必须有自己的实现体

    #       -- 抽象父类中的抽象方法实现体没有意义,实现或不实现都是pass填充

    @abc.abstractmethod

    def func(self):pass

  class Sub(Sup):

    def func(self):

    #必须重写父类的抽象方法

  案例:

  import abc

  class Quan(metaclass=abc.ABCMeta):

    def __init__(self,name):

      self.name = name

    # 共有方法,子类继承就可以了

    def run(self):

      print(self.name + 'running')

    # 抽象方法:子类必须重写

    @abc.abstractmethod

    def chi(self):pass

    @abc.abstractmethod

    def jiao(self):pass

  class Dog(Quan):

    def kanmen(self):

      print(self.name + '看门')

    def chi(self):

      super().chi() # 不执行

      print(self.name + '狗粮')

    def jiao(self):

      print('汪汪汪')

  class Wolf(Quan):

    def bulie(self):

      print(self.name + '捕猎') 

    def chi(self):

      print(self.name + '肉')

    def jiao(self):

      print('嗷嗷嗷')

  dog = Dog('狗')

  wolf = Wolf('狼')

  dog.jiao() # 汪汪汪

  wolf.jiao() # 嗷嗷嗷

  dog.run() # 狗 running

  wolf.run() # 狼 running

  了解:

    # 抽象的类方法   

  import abs
  class Sup(metaclass=abc.ABCMeta):
    @classmethod
    @abc.abstractmethod
    def func(cls): pass

  class Sub(Sup):
    @classmethod
    def func(self):
      # 必须重写父类的抽象方法

四.多态

  多态:对象的多状态 - 父类对象的多种(子类对象)状态

  import abc

  class People(metaclass=abc.ABCMeta):

    def __init__(self,name):

      self.name = name

    @abc.abstractmethod

    def speak(self):pass

  class Chinese(People):

    def speak(self):

      print('说中国话')

  class Engiand(People):

    def speak(self):

      print('说英国话')

  if __name__ == '__main__':

    # 多态的体现:功能或是需求,需要父类的对象,可以传入父类对象或任意子类对象

    #  一般都是规定需要父类对象,传入子类对象

    def ask_someone(obj):

      print('让%s上台演讲'%obj.name) # 父类提供,自己直接继承

      obj.speak() # 父类提供,只不过子类重写了

    ch = Chinese('王大锤')

    en = England('Tom')

    # 传入Chinese|England均可以,应为都是People的一种状态(体现方式)

    ask_someone(ch) # 让王大锤上台演讲 说中国话

    ask_someone(en) # 让Tom上台演讲 说英国话

    # 传入str不可以,因为str的对象没有name和speak

    # s = str('')

五.鸭子类型

  需求:需要一个对象,该对象有name属性和speak方法,就可以作为一种状态的体现被传入

  def ask_someone(obj):

    print('让%s上台演讲'%obj.name)

    obj.speak()

  # 鸭子类型

  # 1.先规定:有什么属性及什么方法的类的类型叫鸭子类型

  # 2.这些类实例化出的对象,都称之为鸭子,都可以作为需求对象的一种具体体现

  class A:

    # 能有自己特有的属性和方法,可以和B完全不一样

     但必须有鸭子类型规定的属性和方法,不然就不是鸭子类型

    def __init__(self,name):

      self.name = name

    def speak(self):

      print('说AAA')

  class B:

    # 能有自己特有的属性和方法,可以和B完全不一样

     但必须有鸭子类型规定的属性和方法,不然就不是鸭子类型

    def __init__(self,name):

      self.name = name

    def speak(self):

      print('说BBB')

  ask_someone(B('B')) # 让B上台演讲 说BBB

六.格式化方法与析构方法

  class A:

    def __init__(self,name,age):

      self.name = name

      self.age = age

    # 格式化方法:在外界打印该类对象是被调用

    # 格式化外界直接打印该类对象的字符串表示结果

    def __str__(self):

      # return 'abc' # 外界打印A类的对象,都打印字符串 abc

      # return super().__str__() # 系统默认的在父类中返回的是对象存放的地址信息

      return '<name:%s | age:%s>' % (self.name, self.age)

    # 根据对象实际的属性格式化具体的输出内容

    #析构方法:在对象被消耗的那一刹那被调用,在被消耗前可以做一些事情

    def __del__(self):

      # del会在self代表的对象被消耗的时候被调用

      # 我们可以在析构函数中释放该对象持有的其他资源

      # 或者将一些持有资源持久化(保存到文件或者数据库中)

      del self.name # 也可以将name存起来

  a = A('老王',88)

  print(a,type(a)) # <name:老王 | age:88> <class '__main__.A'>

  print('文件马上执行完毕,a就会被销毁')

  了解:

  class B:

    # 了解:对象.语法的内部实现

    def __setattr__(self,key,value):

      self.__dict__[key] = value # 系统默认实现,在名称空间添加名字

      # self.__dict__[key] = value.lower() # 可以自定义处理一些内容

    # 了了解:将对象添加属性的方式可以同字典形式

    def __setitem__(self,key,value)

      self.__dict__[key] = value

  b = B()

  # 设置

  b.name = 'BBB' # 内部走的是__setattr__

  b['age'] = 18 # 内部走的是__setitem__

  # 访问

  print(b.name)

  print(b.age)

七.反射

  # 反射:通过字符串与类及类的对象的属性(方法)建立关联

  class A:

    num = 10

  print(hasattr(A,'num'))

  res = getattr(A,'num','默认值')

  print(res)

  delatter(A,'num')

  print(setattr(A,'tag',10))

  # 类的属性类来操作

  class B:

    def __init__(self,name)

      self.name = name

  print(hasattr(b,'name'))

  b = B('BBB')

  print(hasattr(b, 'name'))
  print(getattr(b, 'name', '对象的属性类不能获取'))
  delattr(b, 'name')
  print(setattr(b, 'age', 18))
  # 对象的属性对象来操作

  class C:

    def fn(self):

      print('fn')

    @classmethod

    def func(cls):

      print('func')

  fn = getattr(C,'fn')

  c = C()

  fn(c) # 类获取对象方法调用时传入具体的对象

  obj_fn = getattr(c,'fn')

  obj_fn() # 对象获取对象方法调用时不用传参

  func = getattr(C,'func')

  func() # 类获取类方法调用时不需要传入参数

八.异常处理

  程序运行时的错误
  程序中的异常处理机制:
  1.程序中的所有异常都会被处理
  2.程序中的所有异常都需要手动处理
  3.如果没有手动处理异常,异常会交给Python解释器处理
-- 处理的方式就是打印异常信息,并停止接收器   异常信息的三部分:
  1.异常的追踪信息:提示错误位置
  2.异常的类型:告知处理异常应该捕获什么类型
  3.异常的内容:告知错误信息   处理异常的语法:
  '''
  try:
   # 会出现异常的代码块
  except 异常类型 as 异常别名:
  # 异常处理逻辑
  else:
  # 没有出现异常会执行该分支
  finally:
  # 无论是否出现异常都会执行该分支
  '''   try:
  print(adsdasadsdasadsdas)
  except NameError as e:
  print('异常信息:', e)
  else:
  print('被检测的代码块正常')
  finally:
  print('异常是否出现都会执行该分支')
  print('end')
  1.异常语法详解

    将可能出现异常的代码放在try分支进行检测
      -- 如果不出现异常,正常执行内部所有代码
      -- 如果出现异常会进入except分支

    part1
    # 建议大家对异常处理时,一次只处理一个异常
    try:
      print(asdsdsdsdsdsdsdsdsdsdsdsdsd) # NameError
    except NameError: # except 后跟异常类型,如果不需要查看异常信息,可以省略异常信息
      print('出现了NameError异常')

    try:
      ls = [1, 2, 3, 4, 5]
      print(ls[10]) # IndexError
    except IndexError as e: # 如果想知道异常信息,用别名接收
      print('出现了IndexError异常: %s' % e)

    part2
    # 如果无法避免一句话或是一个完整的代码结构会出现多个可能的异常,需要在一个try中提供多个except
    ls = [1, 2, 3, 4, 5]
    ls = (1, 2, 3, 4, 5, 6)
    try:
      # print(ls[5]) # IndexError
      # ls.append(10) # AttributeError
    except IndexError as e:
      # print('出现了IndexError异常: %s' % e)
    except AttributeError as e:
      # print('出现了AttributeError异常: %s' % e)
    try:
      print(ls[5]) # IndexError
      ls.append(10) # AttributeError
    except (AttributeError, IndexError) as e:
      print('出现了异常: %s' % e)

    part3
    # 3.有些异常提前无法明确,或是压根没有明确的必要,可以捕获异常的父类异常
    ls = [1, 2, 3, 4, 5]
    ls = (1, 2, 3, 4, 5, 6)
    try:
      print(ls[5]) # IndexError
      ls.append(10) # AttributeError
    except Exception as e: # 可以通过多态的应用,捕获父类,只要抛出的是该父类的子类异常,均可以被捕获
      print('出现了异常: %s' % e)

    # BaseException:所有异常的基类 | Exception:常规错误的基类

    part4
    # 4.了了解 - try语法的else分支:当try检测的代码块没有出现异常,才会走else分支
    try:
      print(aaaa)
    except Exception as e:
      print('出现了异常', e)
    else:
      print('没有异常')

    part5
    # 5.finally:无论是否出现异常都会执行该分支
    try:
      f = open('1.txt', 'w', encoding='utf-8')
      f.write(b'123')
    except Exception as e:
      print('出现了异常', e)
    finally:
      print('无论是否出现异常都会执行该分支')
      f.close() # 文件只要打开,不管操作是否出现异常,都需要释放文件资源  

  2.自定义异常

    # 自定义异常的目的:想抛一个有意义的异常,但这个异常系统没有提供,自定义一个
    class PeopleNameError(Exception): # Exception | BaseException
      # pass
    # 可以通过__init__明确外界的错误信息接收给那个属性
    # 再在__str__中格式化外界捕获异常,打印异常信息的格式
      def __init__(self, msg):
        self.msg = msg
      def __str__(self):
        return 'PeopleNameError: ' + self.msg

    def get_name():
    name = input('name: ')
    if 'sb' in name.lower():
      raise PeopleNameError('人名不能有敏感词汇')
    return name

    try:
      print(get_name())
    except PeopleNameError as e:
      print(e) # PeopleNameError: 人名不能有敏感词汇

  3.断言

    num = int(input('num: '))
    assert num < 0 # 断言:只有满足断言条件,程序才能往下执行,反之抛出异常
    print(abs(num))