多态 鸭子类型 反射 内置方法(__str__,__del__) 异常处理
''' 1什么是多态 多态指的是同一种/类事物的不同形态 2 为何要有多态 多态性:在多态的背景下,可以在不用考虑对象具体类型的前提下而直接使用对象 多态性的精髓:统一 多态性的好处: 1增加了程序的灵活性: 以不变应万变,不论对象千变万化,使用者都是同一种形式去调用 2增加了程序可扩展性 继承一个Animal类创建一个新的类,使用者无需更改自己的代码,还是用原来的方式调用 3 如何用多态 ''' ''' class Animal: def speak(self): pass class People(Animal): def speak(self): print('say hello') class Dog(Animal): def speak(self): print('wang wang wang') class Pig(Animal): def speak(self): print('哼哼哼') obj1=People() obj2=Dog() obj3=Pig() # obj1.speak() # obj2.speak() # obj3.speak() def speak(animal): animal.speak() speak(obj1) speak(obj2) speak(obj3) # say hello # wang wang wang # 哼哼哼 多态性带来的好处,比如python的系列类型有多种形态:字符串,列表,元组,多态性提现下 s1='hello' l1=[1,2,3] t1=(1,2) 我们可以在不考虑三者类型的前提下使用是 s l t print(len(s1)) #s1.__len__() print(len(l1)) #l1.__len__() print(len(t1)) #t1.__len__() ''' # import abc # # # class Animal(metaclass=abc.ABCMeta): # @abc.abstractmethod # def speak(self): # pass # # @abc.abstractmethod # def run(self): # pass # Animal() 父类只是用来建立规范的,不能用来实例化的,更无须实现内部的方法 # 只是规定有这些方法或者属性 具体属性或者方法在子类中具体实现 # class People(Animal): # def speak(self): # print('say hello') # # def run(self): # pass # # # class Dog(Animal): # def speak(self): # print('汪汪汪') # # def run(self): # pass # # # class Pig(Animal): # def speak(self): # print("哼哼哼") # # def run(self): # pass # # # obj = People() # obj2 = Dog() # obj3 = Pig() # 上面是强制性的,父类有的方法,子类必须有 # obj.speak() # obj2.speak() # obj3.speak() # say hello # 汪汪汪 # 哼哼哼 # python 崇尚鸭子类型 # 利用标准库中定义的各种‘与文件类似’的对象,尽管这些对象的工作方式像文件,但他们没有继承内置文件对象的方法 # class Disk: # def read(self): # print('Disk read') # # def write(self): # print('Disk write') # # # class Memory: # def read(self): # print('Mem write') # # def write(self): # print('Mem write') # # # class Cpu: # def read(self): # print('Cpu read') # # def write(self): # print('Cpu write') # obj=Disk() # obj2=Memory() # obj3=Cpu() # # obj.read() # obj2.read() # obj3.read() # Disk read # Mem write # Cpu read # 反射: 通过字符串来反射/映射到对象/类的属性上 # (之前是 xx.属性来映射或反射到类或对象的属性上) # class People: # def __init__(self,name,age): # self.name=name # self.age=age # def run(self): # print('%s is running' %self.name) # # # obj=People('egon',18) # print(obj.__dict__) #{'name': 'egon', 'age': 18} # print(obj.name) #obj.__dict__['name'] #egon # obj.name="EGON" #obj.__dict__['name']="EGON" # del obj.name #del obj.__dict__['name'] # print(obj.__dict__) #{'age': 18} # obj.sex='male' #obj.__dict__['sex']='male' # print(hasattr(obj, 'name')) #True 'name' in obj.__dict__ # print(getattr(obj, 'name')) #拿到值egon obj.__dict__['name'] # print(getattr(obj, 'xx', None)) #拿值 没有则返回None obj.__dict__['xx'] # setattr(obj, 'name', 'EGON') #obj.__dict__['name']='EGON' #EGON 有name就改值 # setattr(obj,'xxx','2222') #{'name': 'egon', 'age': 18, 'xxx': '2222'} 没有就创建 # print(obj.name) # print(obj.__dict__) # delattr(obj,'name') # print(obj.__dict__) #{'age': 18} # import os # os.remove # print(hasattr(os,'remove')) #True # class Ftp: # def get(self): # print('get') # def put(self): # print('put') # # def login(self): # print('login') # # def run(self): # while True: # cmd=input(">>>: ").strip() #cmd='get' # if hasattr(self,cmd): #cmd in self.__dict # method=getattr(self,cmd) #self.__dict__['cmd'] # method() # # else: # print('输入方法不存在') # # # obj=Ftp() # obj.run() # 内置方法: # __str__:在对象被打印时自动触发,可以用来定义对象被打印时的输出信息 # 注意:必须返回一个字符串类型的值 # class People: # def __init__(self,name,age): # self.name=name # self.age=age # # def __str__(self): # # print('run.....') # return 'name: %s age:%s'%(self.name,self.age) # obx=People('egon',18) # print(obx) #print(obx.__str__()) # name: egon age:18 # obj1=list([1,2,3]) # print(obj1) #[1, 2, 3] # __del__:在对象被删除时先自动触发该方法,可以用来回收对象以外其他相关资源,比如系统资源 # class Foo: # def __init__(self,x,filepath,encoding='utf-8'): # self.x=x # self.f=open(filepath,'rt',encoding=encoding) # # def __del__(self): # print('run.....') # #回收对象关联的其他资源 # self.f.close() # obj=Foo(1,'a.txt') # print('========》') 1程序结束了会把占用的内存空间回收掉 删除对象 最后执行__del__回收对象关联的其他资源 # ========》 # run..... # obj=Foo(1,'a.txt') #程序没结束提前删除 会先执行__del__ 回收对象关联的其他资源 # del obj # print('========》') # run..... # ========》 ''' 1 什么是异常处理 异常是错误发生的信号,一旦程序出错就会产生一个异常,如果该异常 没有被应用程序处理,那么该异常就会被抛出来,程序的执行也随之终止 异常包含三个部分: 1 traceback异常的追踪信息 2 异常的类型 3 异常的信息 错误分为两大类: 1 语法上的错误:在程序运行前就应该立即修正 2 逻辑上的错误 2 为和要有异常处理 避免程序因为异常而崩溃,所以在应用程序中应该对异常进行处理,从而增强程序的健壮性 3 如何异常处理 try: 代码1 代码2 #抛出一种异常 #!!!!下面代码块不会执行 代码3 。。。 except NameError: #捕获异常也只会捕获一种异常下面的except分支不会执行(像if 。。elif...一样) 当抛出的异常是NameError时执行的子代码块 except....: pass except...: pass else: pass #else的子代码块会在被检测的代码块没有异常的情况下执行 finally: pass #无论被检测的代码有没有异常都会执行 ''' # 语法错误 # print('asdjfkj' # 1 常见的逻辑错误导致的异常 # age=input('>>: ').strip() # print(age>10) #TypeError # for i in 10: #TypeError # pass # import os #AttributeError # os.xxx # 1/0 #ZeroDivisionError # print('====1') # print('====2') # print('====3') # # l=[1,2,3] # # l[100] #IndexError # print('====4') # # d={'x':1,'y':2} # # d['z'] #KeyError # print('====5') # 2.异常处理 # 异常处理的单分支 # try: # print('=====1') # print('=====2') # print('=====3') # d = {'x': 1, 'y': 2} # d['z'] # KeyError # print('=====4') # l = [1, 2, 3] # l[1000] # IndexError # print('=====5') # except IndexError: # print('IndexError') # print('other code') # 异常处理的多分支 # try: # print('=====1') # print('=====2') # print('=====3') # d = {'x': 1, 'y': 2} # d['z'] # KeyError # print('=====4') # l = [1, 2, 3] # l[1000] # IndexError # print('=====5') # except KeyError as e: # print('KeyError',e) # except IndexError as e: # print('IndexError',e) # # print('other code') # try: # print('=====1') # print('=====2') # print('=====3') # d = {'x': 1, 'y': 2} # # d['z'] # KeyError # print('=====4') # l = [1, 2, 3] # l[1000] # IndexError # print('=====5') # except (KeyError,IndexError) as e: #except(里面多个错误捕获类型) # print(e) # print('other code') # 万能异常类型Exception:可以匹配任意类型的异常 # try: # print('=====1') # print('=====2') # print('=====3') # d = {'x': 1, 'y': 2} # # d['z'] # KeyError # # xxx # print('=====4') # l = [1, 2, 3] # l[1000] # IndexError # print('=====5') # except IndexError as e: # print('IndexError:', e) # except KeyError as e: # print('KeyError:', e) # except Exception as e: # print('Exception:',e) # # print('other code') # 主动触发异常 # print('===>1') # print('===>2') # raise TypeError('类型错误') #运行到这里就报错 # print('===>3') # class People: # def __init__(self,name,age): # self.__name=name # self.__age=age # # def tell_info(self): # print(self.__name,self.__age) # # def set_info(self,name,age): # if not isinstance(name,str): # raise TypeError('名字必须是str类型') # if not isinstance(age,int): # raise TypeError('年龄必须是int类型') # self.__name=name # self.__age=age # obj=People('egon',18) # print(obj.__dict__) # {'_People__name': 'egon', '_People__age': 18} # obj.tell_info() #egon 18 # obj.set_info('alex',123) # obj.tell_info() #alex 123 # 自定义异常类型(了解) # class MyException(BaseException): # def __init__(self,msg): # super().__init__() # self.msg=msg # # def __str__(self): #在对象被打印时自动触发,可以用来定义对象被打印时的输出信息 # return '<%s>'%self.msg # # raise MyException('我自定义的异常') # # __main__.MyException: <我自定义的异常> # 断言 print('上半部分,生产数据') l=[1,2,3,4,5] # if len(l)!=5: #满足条件执行里面的 主动触发异常 下面代码不执行 # raise TypeError('类表长度必须为5') assert len(l)==5 #相当于门槛,满足条件才执行下面的代码 #AssertionError print('下半部分,处理数据')
封装:封装的真谛在于明确地区分内外,封装的属性可以直接在内部使用,而不能被外部直接使用,然而定义属性的目的终归是要用,外部想要类隐藏的属性,需要我们为其开辟接口,让外部能够间接地用到我们隐藏起来的属性,意义:
1封装数据:将数据隐藏起来这不是目的。隐藏起来然后对外提供操作该数据的接口,然后我们可以在接口附加对数据的操作限制,以此完成对数据属性操作的严格控制。
class Teacher: def __init__(self,name,age): # self.__name=name # self.__age=age self.set_info(name,age) def tell_info(self): print('姓名:%s,年龄:%s' %(self.__name,self.__age)) def set_info(self,name,age): if not isinstance(name,str): raise TypeError('姓名必须是字符串类型') if not isinstance(age,int): raise TypeError('年龄必须是整型') self.__name=name self.__age=age t=Teacher('egon',18) t.tell_info() t.set_info('egon',19) t.tell_info()
2封装方法:目的是隔离复杂度
在编程语言里,对外提供的接口(接口可理解为一个入口),可以是函数,称为接口函数,这与接口的概念还不一样,接口代表一组接口函数的集合体。
#取款是功能,而这个功能有很多功能组成:插卡、密码认证、输入金额、打印账单、取钱 #对使用者来说,只需要知道取款这个功能即可,其余功能我们都可以隐藏起来,很明显这么做 #隔离了复杂度,同时也提升了安全性 class ATM: def __card(self): print('插卡') def __auth(self): print('用户认证') def __input(self): print('输入取款金额') def __print_bill(self): print('打印账单') def __take_money(self): print('取款') def withdraw(self): self.__card() self.__auth() self.__input() self.__print_bill() self.__take_money() a=ATM() a.withdraw() 隔离复杂度的例子
3在继承中,父类如果不想让子类覆盖自己的方法,可以将方法定义为私有的
#正常情况 >>> class A: ... def fa(self): ... print('from A') ... def test(self): ... self.fa() ... >>> class B(A): ... def fa(self): ... print('from B') ... >>> b=B() >>> b.test() from B #把fa定义成私有的,即__fa >>> class A: ... def __fa(self): #在定义时就变形为_A__fa ... print('from A') ... def test(self): ... self.__fa() #只会与自己所在的类为准,即调用_A__fa ... >>> class B(A): ... def __fa(self): ... print('from B') ... >>> b=B() >>> b.test() from A
隐藏:在python中用双下划线开头的方式将属性隐藏起来(设置成私有的)
这仅仅只是一种变形操作且仅仅只在类定义阶段发生变形
类中所有双下划线开头的名称如__x都会在类定义时自动变形成:_类名__x的形式。
__delattr__() 删除属性前进行的操作需要继承super().__delattr__(属性)否则删除不了
__del__() 删除对象前进行的操作 释放和对象相关的其他资源
__delete__()删除对象的属性(属性是组合类型的 一个类的对象作为另外一个类的属性)