面向对象之组合与封装
一 组合
解决类与类之间代码的冗余问题有两种方式:
1)继承:继承是类与类之间什么是什么的关系,是一种从属关系,子类从属于父类。
2)组合:类与类之间的关系,是一种什么有什么的关系,一个类产生的对象,该对象有一个属性,这个属性的值来自另一个对象。也即是说在一个类中以另一个类的对象作为数据属性,称为类的组合。
下面我们用一个实例来说明下:
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
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('python','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()
当类与类之间显著不同的时候,我们可以可以用组合的方式,减少代码的冗余。
二 封装
我们从字面上来理解封装:就是用一个袋子把一些东西装进去然后把袋子系起来,这样就做到了封装。
我们可以把封装看成一个简单的隐藏,只是改变了隐藏对象的外形,其实实质没变。
其实这仅仅这是一种变形操作且仅仅只在类定义阶段发生变形 类中所有双下划线开头的名称如__x都会在类定义时自动变形成:_类名__x的形式: class A: __N=0 #类的数据属性就应该是共享的,但是语法上是可以把类的数据属性设置成私有的如__N,会变形为_A__N def __init__(self): self.__X=10 #变形为self._A__X def __foo(self): #变形为_A__foo print('from A') def bar(self): self.__foo() #只有在类内部才可以通过__foo的形式访问到. 如果我们一定要从外部访问也是可以访问到的,使用A._A__N是可以访问到的,这种,在外部是无法通过__x这个名字访问到。
那么这种变形我们要注意三个问题:
1)这种封装并不能真正的限制我们从外部直接访问,只是要改变访问方式:_类名__属性,然后就可以访问到这个属性。
2)这种语法意义上的变形,只在类定义阶段发生一次,类定义之后,新增的以__开头的属性都没有变形的效果。
3)如果父类不想让子类覆盖自己的方法,可以在方法前加__开头。
封装数据:
我们上面说封装是一种隐藏,其实严格意义上他不算隐藏,封装的目的是用来被调用的,所以还是要被外部调用,但是只是换了一种方式,不是直接调用而是在调用的过程加上一些限制条件,来完成对于数据属性操作的严格控制。
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()
封装方法:(封装函数属性)
比如我们的相机,为我们提供了拍照功能,其实在拍照的过程中,相机会自动调节一些饱和度,曝光度等,只是我们将这些功能封装起来而已。
提示:在编程语言里,对外提供的接口(接口可理解为了一个入口),可以是函数,称为接口函数,这与接口的概念还不一样,接口代表一组接口函数的集合体。
取款是功能,而这个功能有很多功能组成:插卡、密码认证、输入金额、打印账单、取钱 对使用者来说,只需要知道取款这个功能即可,其余功能我们都可以隐藏起来,很明显这么做 隔离了复杂度,同时也提升了安全性 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()