组合、封装、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

  

  

原文地址:https://www.cnblogs.com/xiaocaiyang/p/9845618.html