Python--(类和对象 下篇)

面向对象的三大特征:封装、继承、多态

封装:隐藏内部实现 提供外部接口 易于修改,类的内部修改并不影响外部更好地保护类或对象的属性值的安全

(代码中:if __name__=='__main__'代表程序主入口是从这里进入的)

一、封装(接上篇部分)

<1>类属性:属于类的成员,对象共有的

修改方式: 类名.类属性=.../实例对象.__class__类属性

        类属性对象属性比较使用:

        类属性可以全部使用,而对象属性仅限于自己对象的使用

例:

 
class Person:
    def __init__(self):
        self.name='无名氏'
        self.age=18
    def show(self):
        print('姓名:'+self.name+'年龄:',self
              .age)
if '__main__'==__name__:
    Person.phone='110'  #类属性给参数赋值
    zhangsan=Person()
    zhangsan.name='张三'
    zhangsan.age=20
    zhangsan.gender='男'  #对象属性 当两个对象需要区分时使用对象属性
    zhangsan.show()
    zhangsan.__class__.phone='123456'  #当作为全局都能够访问时用类属性/给一个类赋值所以下面的李四电话显示与张三相同
    print('zhangsan.gender'+zhangsan.gender)
    print(zhangsan.__class__.phone)

    lisi = Person()
    lisi.name = '李四'
    lisi.age = 20
    lisi.show()
    # lisi.__class__.phone = '520520'  #取消注释会显示电话为520520
    print(lisi.__class__.phone)

执行命令得

 
姓名:张三年龄: 20
zhangsan.gender男
123456
姓名:李四年龄: 20
123456

<2>类方法:用于拓展原来函数功能的一种函数

在方法上添加@classmethod 装饰器(类方法更倾向于工具方法,不需要重复创建对象.不是以对象的形式去调用)

       用法:

@classmethod

def class_method(cls):

特点:可以通过类方法调用类属性,也可以通过对象调用类属性

 
class Person:
    @classmethod
    def sayHello(cls):
        print('这个是一个Person类')
if '__main__'==__name__:
    Person.sayHello()

执行命令得

这个是一个Person类

<3>静态方法:

方法前加@staticmethod,(静态方法可以加参数,静态方法既和类没关系,也和对象没关系,也可以通过类和对象调用)

一般情况下,静态方法会单独放在一个模块里

例:

 
class Person:
    @staticmethod
    def sayGood(say):#括号里可填写可不填写
        print(say,'GOOD')
if '__main__'==__name__:
    Person.sayGood(123)#上面的括号里填写了参数这里也必须填写内容否则报错

执行命令得

 
123 GOOD

二、继承

1.子类(父类)

概念:子类继承父类.子类可以使用父类的属性和方法,简化代码

当生成子类对象时,先初始化父类对象.如果父类有__init__()方法,并且有属性时,要通过子类的构造赋值.一个类可以有多个子类

初始化时,

子类中调用父类的属性时,在__init__()方法中使用四种方法如下:

<1>super().__init__(属性)(调用有参)切记不能在属性前加self 否则报错

<2>self.属性=''一般不使用这类形式已在父类中存在

<3>父类.__init__(self,属性)(调用有参)切记必须在属性前加self否则报错

       父类.__init__(self)(调用无参)

<4>super(子类,self).__init__(属性)

子类中调用父类的方法时的用法:super().父类方法()

例:

 
class Pet:
    def __init__(self,name,love,health):
        '''
        快速初始化自定义数据
        :param name:
        :param love:
        :param health:
        '''
        self.name=name
        self.love=love
        self.health=health
    def show(self):
        print('宠物名'+self.name+' 健康值',self.health,'亲密度:',self.love)
class Dog(Pet):
    def __init__(self,name,love,health):
        # <1>super().__init__(name,love,health)
        # <2>通过self.name='可乐'形式逐个代替
        # <3>Pet.__init__(self,name,love,health)
        super(Dog, self).__init__(name,love,health)
        self.strain='二哈'
    def eat(self):
        print(self.strain+'  正在吃饭')
    def showInfo(self):
        super().show()
        print('品种: ',self.strain)
if __name__=='__main__':
   dog=Dog('可乐 ',100,0)
   dog.eat()
   dog.show()
   dog.showInfo()

执行命令得

 
二哈  正在吃饭
宠物名可乐  健康值 0 亲密度: 100
宠物名可乐  健康值 0 亲密度: 100
品种:  二哈

练习

交通工具类:属性:名称 方法:行驶
子类:卡车,属性:载重,重写行驶的方法
子类:火车,属性:车箱个数,重写行驶的方法

 
class Traffic:
    def __init__(self,name):
        self.name=name
        self.way='travel'
    def run(self):
        print('交通工具'+self.name+'正在行驶')
class Truck(Traffic):
    def __init__(self,name,weight):
        super(Truck, self).__init__(name)
        self.weight=weight
    def runInfo(self):
        super().run()
        print('卡车'+self.name+'载重'+self.weight+'飞驰')
class Train(Traffic):
    def __init__(self,name,num):
        super(Train, self).__init__(name)
        self.num=num
    def runInfo(self):
        super().run()
        print('火车'+self.name+'有'+str(self.num)+'节车厢')
if '__main__'==__name__:
    kache=Truck('一汽','1吨')
    kache.runInfo()

    huoche=Train('和谐号',15)
    huoche.runInfo()

执行命令得

 
交通工具一汽正在行驶
卡车一汽载重1吨飞驰
交通工具和谐号正在行驶
火车和谐号有15节车厢

总结:

当子类继承父类时,子类的构造方法应该包含父类和子类共同的属性,在子类的初始化__init__()方法中,将父类属性传递给父类,子类的属性赋值给子类(即name为父类属性传递给父类 weight和num为自己独特属性复制给自己)

方法重写:

子类继承父类时,子类的方法签名和父类一样,此时子类重写了父类的方法,当生成子类对象时,调用的格式子类重写得方法

2.继承的特性之一:传递性

三代继承:子类初始化方法需要祖父、父类及自己的属性,可以调用父类的初始化方法传参,可以重写父类的方法

构造顺序:祖父类-->父类-->自己

 
class Pet:
    def __init__(self,name,love,health):
        self.name=name
        self.love=love
        self.health=health
        print('pet')
    def show(self):
        print('宠物名'+self.name+' 健康值',self.health,'亲密度:',self.love)
class Dog(Pet):
    def __init__(self,name,love,health):
        super(Dog, self).__init__(name,love,health)
        self.strain='泰迪'
        print('dog')
    def eat(self):
        print(self.strain+'  正在吃饭')
    def showInfo(self):
        super().show()
        print('品种: ',self.strain)
class Taidi(Dog):
    def __init__(self,name,love,health):
        super(Taidi, self).__init__(name,love,health)
        print('taidi')
if __name__=='__main__':
   dog=Taidi('可乐 ',100,0)
   dog.eat()
   dog.showInfo()

执行命令得

 
pet
dog
taidi #pet. dog. taidi打印的先后顺序代表构造顺序
泰迪  正在吃饭
宠物名可乐  健康值 0 亲密度: 100
品种:  泰迪

类根源顶级父类-->继承object

被继承的类叫:基类 父类

继承的类叫:子类 派生类

方法重写:

如果子类重写的方法想调用父类方法时,在子类方法中写法:父类.方法(self)或super().父类方法()


3.私有属性、私有方法均不能在类外面被调用

多继承:类同时继成多个父类 如:class C(A,B),当有AB均有相同方法,而子类又重写时,调用水的方法(子类)如果子类没有重写方法,则调用那个父类的方法?(左侧,优先继承得类)

 
class Pet:
    def __init__(self,name,love,health):
        self.name=name
        self.love=love
        self.health=health
    def show(self):
        print('宠物名'+self.name+' 健康值',self.health,'亲密度:',self.love)
class Dog(Pet):
    def __init__(self,name,love,health):
        super(Dog, self).__init__(name,love,health)
        self.strain='哈士奇'
    def showInfo(self):
        super().show()
        print('品种: ',self.strain)
class lang:
    def show(self):
        print('我是一匹来自北方的狼')
class Hashiqi(lang,Dog):#Hashiqi中没有重写show时调用左侧的lang父类
    def __init__(self,name,love,health):
        super(Hashiqi, self).__init__(name,love,health)
if __name__=='__main__':
   dog=Hashiqi('可乐 ',100,0)
   dog.showInfo()
   dog.show()

执行命令得

 
哈士奇  正在吃饭
宠物名可乐  健康值 0 亲密度: 100
品种:  哈士奇
我是一匹来自北方的狼

类名.mro(),可以看到所有父类,及搜索顺序
这就是动态语言的"鸭子类型",她并不要求严格的继承体系,一个对象只要"看起来像鸭子,走起路来像鸭子",那他就可以被看做鸭子

练习

(图书\DVD管理系统)

----DVD管理系统----:
1.查询所有DVD
2.增加DVD
3.借出DVD
4.归还DVD
5.退出

 
class DVD:
    def __init__(self,name,price,state):
        self.name=name
        self.price=price
        self.state=state
if '__main__'==__name__:
    xiyouji=DVD('西游记',100,0)#0代表未借出 1 代表已借出
    sanguo=DVD('三国',100,1)
    shuihuzhuan=DVD('水浒传',100,0)
    dvds={xiyouji.name:xiyouji,sanguo.name:sanguo,shuihuzhuan.name:shuihuzhuan}
    while True:
        print('1.查询所有DVD')
        print('2.增加DVD')
        print('3.借出DVD')
        print('4.归还DVD')
        print('5.退出')
        num=int(input('请输入数字:'))
        if num==1:
            print('名称\t价格\t状态')
            for key in dvds.keys():
                if dvds.get(key).state==0:
                    print(key+' \t'+str(dvds.get(key).price)+' \t'+'未借出')
                else:
                    print(key+' \t'+str(dvds.get(key).price)+' \t'+'已借出')
        elif num==2:
            name=input('请输入新的DVD:')
            while name in dvds.keys():
                name=input('此DVD已存在 请重新输入名字:')
            price=int(input('请输入新DVD价格:'))
            new_name=DVD(name,price,0)
            dvds[name]=new_name
            print('增加成功')
        elif num==3:
            name = input('请输入你所需要的DVD:')
            while name not in dvds.keys():
                name=input('此DVD不存在 请重新输入名字:')

            if dvds.get(name).state==1:
                print(name,'已借出')
            else:
                dvds.get(name).state=1
                print('借出成功')
        elif num==4:
            name = input('请输入你所归还的DVD:')
            while name not in dvds.keys():
                name=input('此DVD不存在,请重新输入DVD名字')
            if dvds.get(name).state==0:
                print('此DVD未借出')
            else:
                dvds.get(name).state=0
                date=int(input('请输入借书日期'))
                print('扫一扫付款',int(date*int(dvds.get(name).price)),'元')
                print('归还成功')
        elif num==5:
            print('欢迎使用')
            break

4.__new__(cls):#用来创建对象,而且必须有返回值

return object.__new__(cls);

#return super(子类,cls).__new__(cls,args,kwargs)

可以用id(cls)看地址

当有属性时,需要在__new__()中也添加属性

单例模式: 该模式的主要目的是确保某一个类只有一个实例存在(实例等同于一个对象等同于一个内存地址等同于一块空间)

应用:资源共享

 
class singleton:
    __instace=None
    def __new__(cls, *args, **kwargs):
        print('__new__')
        if cls.__instace==None: #说明没有创建过
            cls.__instace=object.__new__(cls)
            return cls.__instace
        else:#创建过
            return cls.__instace
    def __init__(self):
        print('__init__')
s1=singleton()
s2=singleton()
s3=singleton()
print(id(s1))
print(id(s2))
print(id(s3))

执行命令得

 
__new__
__init__
__new__
__init__
__new__
__init__
77853488
77853488
77853488

三、多态

多态:多种形态 .不同的类对同一个消息/动作做出不同的解释执行不一样的代码

多态的3个必要条件:1.发生继承关系2.子类重写父类方法3.里氏代换原则

使用方式:以父类类型作为形参

<1>工厂类:在一个类中生成很多对象,简单工厂模式(Simple Factory Pattern)

          是通过专门定义一个类来负责创建其他类的实例,被创建的事例同床都具有共同得父类 ,并且重写父类方法.

四则运算的父类,接受用户输入得数值 Operation 

OperationAdd  

OperationSub

 OperationMul 

OperationDiv 

OperationFactroy(object)

 
# 四则运算得父类 ,接受用户输入的数值Operation
class Operation:#四则运算父类
    def __init__(self,num1,num2):
        self.num1=num1
        self.num2=num2
    def yunsuan(self):
        pass
class OperationAdd(Operation):
    def __init__(self,num1,num2):
        super(OperationAdd, self).__init__(num1,num2)
    def yunsuan(self):
        return self.num1 + self.num2
class OperationSub(Operation):
    def __init__(self,num1,num2):
        super(OperationSub, self).__init__(num1,num2)
    def yunsuan(self):
        return self.num1 - self.num2
class OperationMul(Operation):
    def __init__(self,num1,num2):
        super(OperationMul, self).__init__(num1,num2)
    def yunsuan(self):
        return self.num1 * self.num2
class OperationDiv(Operation):
    def __init__(self,num1,num2):
        super(OperationDiv, self).__init__(num1,num2)
    def yunsuan(self):
        return self.num1 // self.num2
#工厂类  需要加减乘除时 工厂类来进行创建
class OperationFactory(object):
    @classmethod#添加修饰器 类名.方法即可调用
    def getOperation(self,sign,num1,num2):#获取运算符算的方法
        if '+'.__eq__(sign):
            return OperationAdd(num1,num2)
        if '-'.__eq__(sign):
            return OperationSub(num1,num2)
        if '*'.__eq__(sign):
            return OperationMul(num1,num2)
        if '/'.__eq__(sign):
            return OperationDiv(num1,num2)
if '__main__'==__name__:
    num1=int(input('请输入第一个操作数:'))

    num2 = int(input('请输入第二个操作数:'))
    sign = input('请输入操作符:')
    #返回与操作符对应的 运算对象
    Operation=OperationFactory.getOperation(sign,num1,num2)
    result=Operation.yunsuan()
    print('运算结果:',result)

<2>isinstance()函数 判断是否继承关系或者某一个变量是否是某一个数据类型

 
#连接上面的加减乘除为例
'''
'''
if '__main__'==__name__:
    num1=int(input('请输入第一个操作数:'))

    num2 = int(input('请输入第二个操作数:'))
    sign = input('请输入操作符:')
    #返回与操作符对应的 运算对象
    Oper=OperationFactory.getOperation(sign,num1,num2)
    result=Oper.yunsuan()
    print('运算结果:',result)
    # 判断是否是Oper得数据类型
    if isinstance(Oper,OperationAdd):
        print('创建的是OperationAdd 类型的对象')
    if isinstance(Oper,OperationSub):
        print('创建的是OperationSub 类型的对象')
    if isinstance(Oper,OperationMul):
        print('创建的是OperationMul 类型的对象')
    if isinstance(Oper,OperationDiv):
        print('创建的是OperationDiv 类型的对象')
执行命令得
 
创建的是OperationAdd 类型的对象
创建的是OperationSub 类型的对象
创建的是OperationMul 类型的对象
创建的是OperationDiv 类型的对象

判断是否继承关系

 
'''
'''
    print(isinstance(Oper,Operation))
    print(isinstance(Oper,object))

执行命令得

 
True
True


<3>获取类名,对象名,属性名

getattr(类名/对象名)  setattr(类名/对象名)  

hasattr(类名/对象名,'属性名')只能判断不能获取

用法

 
'''
'''
    #获取属性值
    Oper=OperationFactory.getOperation('+',1,5)
    print(Oper.__getattribute__('num2'))
    #当所要的是属性值没有时
    Oper.__setattr__('num3',7)
    print(Oper.__getattribute__('num3'))
    print(hasattr(Oper,'num3'))

执行命令得

 
5
7
True

<4>动态语言

为类动态添加函数 所有的对象都能用

用法:

 
#以加减乘除为例
class Operation:'''
class OperationAdd(Operation):'''
class OperationSub(Operation):'''
class OperationMul(Operation):'''
class OperationDiv(Operation):'''
class OperationFactory(object):
def setName(self,name):#动态添加函数(方法)
    self.name=name
if '__main__'==__name__:
    Oper=OperationFactory.getOperation('+',1,5)
    Oper2=OperationFactory.getOperation('-',10,5)
    Operation.setName=setName
    Oper.setName('加法')
    print('运算方法:'+Oper.name)
    Oper2.setName('减法')
    print('运算方法:'+Oper2.name)

执行命令得

 
运算方法:加法
运算方法:减法

<5>__call__直接在实例本身上调用

    通过callable()函数,我们就可以判断一个对象是否是可调用的对象

 
class Person:
    def __call__(self, *args, **kwargs):
        print('执行了call函数')
if '__main__'.__eq__(__name__):
    zhangsan=Person()
    zhangsan()# 或者填写print(callable(zhangsan))

执行命令得

 
执行了call函数#True

<6>@property得使用

@property 注解优化getter  setter-->@函数者.setter

@property装饰器是负责把一个方法变成属性调用,它的实现比较复杂,把一个getter方法变成属性,只需要加上@property就可以了,此时,@property本身创建了另一个装饰器@xx.setter,负责把一个setter方法变成属性赋值,于是,我们就拥有一个可控的属性操作

 
class Person:
    @property
    def Age(self):
        return self.age
    @Age.setter
    def Age(self,age):
        if age<0 or age>100:
            self.age=18
        else:
            self.age=age
            print('赋值了',self.age)
if '__main__'.__eq__(__name__):
     zhangsan=Person()
     zhangsan.Age=30
     print(zhangsan.Age)

执行命令得

 
赋值了 30
30

<7>动态语言的灵活性,为了达到限制的目的,Python允许在定义class得时候定义一个特殊的__slots__变量.来限制该class实例能添加的属性

 
class student:
    __slots__ = ('name','age')
stu=student()
stu.name='zhangsan'
stu.score=99

执行命令得

 
#报错AttributeError: 'student' object has no attribute 'score'

四、里氏替换原则

1.子类可以实现父类的抽象方法,但是不能覆盖父类的非抽象方法

2.子类中可以增加自己特有方法

3.当子类覆盖或实现父类得方法时,方法的前置条件(即方法的形参)要比父类方法的输入参数更宽松

4.当子类的方法实现父类得抽象方法时,方法的后置条件(及方法的返回值)要比父类更严格

始终记住里氏替换原则的最终目的就是保持父类的方法不被覆盖




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章