面向对象:接口思想、多态、鸭子类型、反射

2020-01-16 17:44:12 浏览数 (2)

一、接口思想

  • 建立关联的桥梁,方便管理代码
  • 接口思想提现:为类拓展功能
  • 接口类:用来定义功能的类,为继承它的子类提供功能的。 该类的功能方法一般不需要有实现体,实现体有继承它的子类自己去实现。
代码语言:javascript复制
#提供所有看门应该有的功能
class WatchInterface:
    def watch_door(self):
        pass

#没有去继承PetInterface,WatchInterface的Dog就是普通的Dog类
#但继承了PetInterface,该Dog就可以作为宠物狗,同理继承WatchInterface就可以作为看门狗
class Dog(PetIterface,WatchInterface):
    def jiao(self):
        pass
    
    def chi(self):
        pass
    
    def pao(self):
        pass
    
    #可以作为宠物及看门猫
    class Cat(PetIterface,WatchInterface):
        pass

二、多态(重点)

定义:多态指的是一类事物有多种形态。

  动物有多种形态:人,狗,猪

  文件有多种形态:文本文件,可执行文件

  相当于我们在父类中定义一个统一的多个共同形态的方法,比如人狗猪都能吃跑叫这些方法,我们在父类将其方法进行抽象,即抽象方法,这种方法的实现体是抽象的,也就是说只写方法函数名,具体代码块实现不写,交由子类重写具体实现体。

  具体实现方法:  

  • import abc            #abstract base class
  • class Sup(metaclass=abc.ABCMeta):            #Sup为父类
  • 将抽离对象加装饰器@abc.abstractmethod
    • 子类对应方法必须重写,必须有自己的实现体,不然报错
    • 抽象父类中的抽象方法实现体无意义,实现不实现一样。
    • 注意点:有抽象方法的父类不能被实例化

简言之:我在父类定一些可以抽离的公共方法的模板,你们下面的子类必须按照子类的这个模板方法去执行,至于具体执行实现体代码输出信息你们子类自己根据自己的情况去输出想要的信息。但是模板必须有样,做不到我就给你报错。

看下面的例子:

代码语言:javascript复制
import abc
class Quan(metaclass=abc.ABCMeta):
    def __init__(self,name):
        self.name=name
    #共有方法,模板来了,小的们,照这个模板去开头,具体实现体你们去重写吧,我不管
    @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):
        super().chi()
        print(self.name   '吃肉')

    def jiao(self):
        print('嗷嗷嗷')

dog=Dog('来福')
wolf=Wolf('常威')

dog.jiao()
wolf.jiao()
dog.chi()
wolf.chi()

在面向对象方法中一般是这样表述多态性:

    向不同的对象发送同一条消息(!!!obj.func():是调用了obj的方法func,又称为向obj发送了一条消息func),不同的对象在接收时会产生不同的行为(即方法)。也就是说,每个对象可以用自己的方式去响应共同的消息。所谓消息,就是调用函数,不同的行为就是指不同的实现,即执行不同的函数。

    比如:老师.下课铃响了(),学生.下课铃响了(),老师执行的是下班操作,学生执行的是放学操作,虽然二者消息一样,但是执行的效果不同

总结:多态性的好处:

  • 增加了程序的灵活性:以不变应万变,不论对象千变万化,使用者都是同一种形式去调用
  • 增加了程序的可扩展性

三、鸭子类型

如果看起来像、叫声像而且走起路来像鸭子,那么它就是鸭子

代码语言:javascript复制
#二者都像鸭子,二者看起来都像文件,因而就可以当文件一样去用
class TxtFile:
    def read(self):
        pass

    def write(self):
        pass

class DiskFile:
    def read(self):
        pass
    def write(self):
        pass

四、反射

反射:通过字符串来反射/映射到对象/类的属性上

代码语言:javascript复制
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)

# ------------------------------------------------------------
# hasattr:查看对象中是否有某个属性(属性应该为字符串格式的属性名),有返回True,没有返回False
print(hasattr(obj,'name'))
#结果为:True
#判断对象名obj中是否有'name'这个默认属性,相当于'name' in obj.__dict__

# ------------------------------------------------------------
# getattr:取出对象中某个属性的值(属性应该为字符串格式的属性名),如果没有该属性,报错,可以设置无该属性时候的返回值
print(getattr(obj,'name'))
#结果为:egon
#取出对象名obj中属性'name'的值,相当于obj.__dict__['name']
print(getattr(obj,'xxx','没有该对象'))
#结果为:没有该对象
#取出对象名obj中属性'xxx'的值,如果没有则返回'没有该对象'

# ------------------------------------------------------------
# setattr:将对象中的某个属性赋予新值(属性应该为字符串格式的属性名),如果该属性无,添加属性以及值
setattr(obj,'name','EGON')
#将原对象中的'name'赋予新值'EGON',相当于obj.__dict__['name']='EGON'
setattr(obj,'xxx',1111)
#xxx不在原对象名称空间,则新增属性xxx并赋予值1111,相当于obj.__dict__['xxx']=1111
print(obj.name)
#结果为:EGON
print(obj.__dict__)
#结果为:{'name':'EGON','age':18,'xxx':1111}

# ------------------------------------------------------------
# delattr:删除对象中的某个属性(属性应该为字符串格式的属性名)
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()
            if hasattr(self,cmd):
                method=getattr(self,cmd)
                method()
            else:
                print('输入的方法不存在')

# obj=Ftp()
# obj.run()
'''
# 结果:
# >>>>>:get
# 运行get函数
# >>>>>:put
# 运行put函数
# >>>>>:login
# 运行login函数
# >>>>>:hah
# 输入的方法不存在
# .....

'''

五、内置方法

代码语言:javascript复制
'''
1__str__: 在对象被打印时自动触发,可以用来定义对象被打印时的输出信息
#  注意:必须返回一个字符串类型的值
class People:
    def __init__(self,name,age):
        self.name=name
        self.age=age

    def __str__(self):
        return 'name:%s age:%s'%(self.name,self.age)

obj1=People('egon',18)
print(obj1)  #实际上在执行:print(obj1.__str__())
#结果为:name:egon age:18

obj2=list([1,2,3])
print(obj2)
结果为:[1, 2, 3]
'''
# 2__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('runing....')
        #回收对象关联的其他资源
        self.f.close()

obj=Foo(1,'a.txt')
# del obj
print('=========>')

# 3__call__: 在对象被调用时会自动触发该方法,可以用来???
class Foo:
    def __init__(self,x,y):
        self.x=x
        self.y=y

    def __call__(self, *args, **kwargs):
        print(self,args,kwargs)

obj=Foo(1,2)
obj(1,2,a=3,b=4)  #相当于obj.__call__(obj,1,2,a=3,b=4)
# 结果为:<__main__.Foo object at 0x000001B6ABA35B00> (1, 2) {'a': 3, 'b': 4}

六、异常处理

  • 程序运行时的错误
  • 程序中的异常处理机制:
  1. 程序中的所有异常都会被处理
  2. 程序中的所有异常都需要手动处理
  3. 如果没有手动处理异常,异常会交给Python解释器处理
  • 处理方式就是打印异常信息,并停止接收器
  • 异常的信息的三部分:
  1. 异常的追踪信息:提示错误位置
  2. 异常的类型:告知处理异常应该捕获什么类型
  3. 异常的内容:告知错误信息

处理异常的语法:

代码语言:javascript复制
try:
    #会出现异常的代码块

except '异常类型' as '异常别名':
    #异常处理逻辑

else:
    #没有出现异常会执行该分支

finally:
    #无论是否出现异常都会执行该分支

实例:

代码语言:javascript复制
try:
    print(adadadadada)

except NameError as e:
    print('异常信息:',e)

else:
    print('被检测的代码块正常')

finally:
    print('异常是否出现都会执行该分支')

print('结束')
#结果:
异常信息: name 'adadadadada' is not defined
异常是否出现都会执行该分支
结束

0 人点赞