zoukankan      html  css  js  c++  java
  • 组合、封装、property装饰器和多态

      今天学习了组合、封装、property装饰器和多态

      一、组合

        软件重用的重要方式除了继承之外还有另外一种方式,即:组合

        组合指的是,在一个类中以另外一个类的对象作为数据属性,称为类的组合

    class Equip:    #武器装备类
        def fire(self):
            print('release Fire skill')
        
    class Riven:    #英雄Riven的类,一个英雄需要有装备,因而需要组合Equip类
        camp = 'Noxus'
        def __init__(self,nickname):
            self.nickname = nickname
            self.equip = Equip()    #用Equip类产生一个装备,赋值给实例的equip属性
    
    >>>r1 = Riven('锐雯雯')
    >>>r1.equip.fire()    #可以使用组合的类产生的对象所持有的方法
    release Fire skill

    组合与继承都是有效利用已有类的资源的重要方式。但是二者的概念和使用场景皆不同,

    1.继承的方式

    通过继承建立了派生类与基类之间的关系,它是一种‘是’的关系,比如白马是马,人是动物。

    当类之间有很多相同的功能,提取这些共同的功能做成基类,用继承比较好,比如老师是人,学生是人

    2.组合的方式

    用组合的方式建立了类与组合的类之间的关系,它是一种‘有’的关系,比如教授有生日,教授教python和linux课程,教授有学生s1、s2、s3。。。

    class People:
        def __init__(self,name,age,sex):
            self.name = name
            self.age =age
            self.sex = sex
    
    class Course:
        def __init__(self,name,period,price):
            self.name = name
            self.period = period
            self.price = price
        def tell_info(self):
            print('<%s %s %s>'%(self.name,self.period,self.price))
    
    class Teacher(People):
        def __init__(self,name,age,sex,job_title):
            People.__init__(self,name,age,sex)
            self.job_title = job_title
            self.course = []
            self.students = []
    
    class Student(People):
        def __init__(self,name,age,sex):
            People.__init__(self,name,age,sex)
            self.course = []
        
    egon = Teacher('egon',18,'male','沙河霸道金牌讲师')
    s1 = Student('牛榴弹',18,'female')
    
    python = Course('python','3mons',3000.0)
    linux = Course('linux','3mons',3000.0)
    
    #为老师egon和学生s1添加课程
    egon.course.append(python)
    egon.course.append(linux)
    s1.course.append(python)
    
    #为老师egon添加学生s1
    egon.students.append(s1)
    
    #使用
    for obj in egon.course:
        obj.tell_info()

    当类之间有显著不同,并且较小的类是较大的类所需要的组件时,用组合比较好

      二、封装

        从封装本身的意思去理解,封装就好像是拿来一个麻袋,将好多东西一起装进麻袋,然后把麻袋封上口子。照这种逻辑看,封装='隐藏',这种理解是相当片面的。

        在python中用双下划线开头的方式将属性隐藏起来(设置成私有的)

        

    #其实这仅仅只是一种变形操作且仅仅只在类定义阶段发生变形
    #类中所有双下划线开头的名称如__x都会在类定义时自动变形成:_类名__x的形式
    class A:
        __N = 0    #类的数据属性就应该是共享的,但是语法上是可以把类的数据属性设置成私有的如__N,会变形为_A__N
        def __init__(self):
            self.__X = 10    #变形为self._A__X
        def __foo(self):    #变形为self._A__foo
            print('from A')
        def bar(self):
            self.__foo()    #只有在类内部才可以通过__foo的形式访问到。
    #A._A__N是可以访问到的,
    #这种,在外部是无法通过__x这个名字访问到。

        这种变形需要注意的问题是:

        1.这种机制也并没有真正意义上限制我们从外部直接访问属性,知道了类名和属性名就可以拼出名字:_类名__属性,然后就可以访问了,如a._A__N,即这种操作并不是严格意义上的限制外部访问,仅仅只是一种语法意义上的变形,主要用来限制外部的直接访问。

        2.变形的过程只在类的定义时发生一次,在定以后的赋值操作,不会变形

        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

      三、封装不是单独意义的隐藏

        封装的真谛在于明确地区分内外,封装的属性可以直接在内部使用,而不能被外部直接使用,然而定义属性的目的终归是要用,外部要想要类隐藏的属性,需要我们为其开辟接口,让外部能够间接地用到我们隐藏起来的属性,那这么做的意义何在???

      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('年龄必须是整型')
            
    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()

      四、特征(property)

      什么是特征property

        property是一种特殊的属性,访问它时会执行一段功能(函数)然后返回值

      例:BMI指数(BMI是计算而来的,但很明显它听起来像是一个属性而非方法,如果我们将其做成一个属性,更便于理解)  

      成人的BMI数值:

      过轻:低于18.5

      正常:18.5-23.9

      过重:24-27

      肥胖:28-32

      非常肥胖:高于32

        体质指数(BMI)=体重(kg) / 身高^2(m)

        EX:70kg / (1.75*1.75) = 22.86

    class People:
        def __init__(self,name,weight,height)
            self.name = name
            self.weight = weight
            self.height = height
        @property
        def bmi(self):
            return self.weight / (self.height**2)
    
    p1 = People('egon',75,1.85)
    print(p1.bmi)

      为什么要用property

      将一个类的函数定义成特征以后,对象再去使用的时候obj.name,根本无法察觉自己的name是执行了一个函数然后计算出来的,这种特性的使用方式遵循了统一访问的原则

      五、多态性

      一、什么是多态动态绑定(在继承的背景下使用时,有时也称为多态性)

      多态性是指在不考虑实例类型的情况下使用实例

      

    在面下对象方法中一般是这样表述多态性:向不同的对象发送同一条消息(!!! obj.func():是调用了obj的方法func,
    又称为向obj发送了一条消息func),不同的对象在接受时会产生不同的行为(即方法)。也就是说,每个对象可以用自己
    的方法去响应共同的消息。所谓消息,就是调用函数,不同的行为就是指不同的实现,即执行不同的函数。 比如:老师.下课铃响了(),学生.下课铃响了(),老师执行的是下班操作,学生执行的是放学操作,虽然二者消息一样,
    但是执行的效果不同。

      多态性分为静态多态性和动态多态性

        静态多态性,如任何类型都可以用运算符+进行运算

        动态多态性:如下

    peo = People()
    dog = Dog()
    pig = Pig()
    #peo、dog、pig 都是动物,只要是动物肯定有talk方法
    #于是我们可以不用考虑它们三者的具体是什么类型,而直接使用
    peo.talk()
    dog.talk()
    pig.talk()
    
    #更进一步,我们可以定义一个统一的接口来使用
    def func(obj):
        obj.talk()

      二、为什么要用多态性(多态性的好处)

      1.增加了程序的灵活性

        以不变应万变,不论对象千变万化,使用者都是同一种形式去调用,如func(animal)

      2.增加了程序可扩展性

        通过继承animal类创建了一个新的类,使用者无需更改自己的代码,还是用func(animal)去调用

    >>>class Cat(animal):    #属于动物的另外一种形态:猫
                def talk(self):
                    print('say miao')
    >>>def func(animal):    #对于使用者来说,自己的代码根本无需改动
                animal.talk()
        
    >>>cat1 = Cat()    #实例出一只猫
    >>>fun(cat1)    #甚至连调用方式也无需改变,就能调用猫的talk功能
    say miao
    
    '''
    这样我们新增了一个形态Cat,由Cat类产生的实例cat1,使用者可以在完全不
    需要修改自己代码的情况下。使用和人、狗、猪一样的方式cat1的talk方法,即func(cat1)
    '''

      三 鸭子类型

      逗比时刻:

        Python崇尚鸭子类型,即如果看起来像、叫声像而且走起路来像鸭子,那么它就是鸭子

      python程序员通常根据这种行为来编写程序。例如,如果想编写现有对象的自定义版本,可以继承该对象也可以创建一个外观和行为像,但与它无任何关系的全新对象,后者通常用于保存程序组件的松耦合度。

      例:利用标准库中定义的各种'与文件类似'的对象,尽管这些对象的工作方式像文件,但他们没有继承内置文件对象的方法

    #二者都像鸭子,二者看起来都像文件,因而就可以当文件一样去用
    class TxtFile:
        def read(self):
            pass
        
        def write(self):
            pass
    
    class DiskFile:
        def read(self):
            pass
        def write(self):
            pass

      

      

  • 相关阅读:
    TreeView使用集锦
    net 下安装、调试的常见问题与错误及解决方法 [转]
    Oracle中使用同义词
    再推荐两个blog和一首好歌
    一点感慨
    推荐一个blog和一个工具
    买书了
    第一次做饭
    ORA00911错误及解决方法
    C#3.0美文收集
  • 原文地址:https://www.cnblogs.com/xiaocaiyang/p/9845618.html
Copyright © 2011-2022 走看看