python 之 面向对象基础(组合和封装)
7.4 组合
解决类与类之间代码冗余问题有两种解决方案:
1、继承:描述的是类与类之间,什么是什么的关系
2、组合:描述的是类与类之间的关系,是一种什么有什么的关系
一个类产生的对象,该对象拥有一个属性,这个属性的值是来自于另外一个类的对象
class Date: def __init__(self,year,mon,day): self.year = year self.mon = mon self.day = day def tell_birth(self): print('出生年月日<%s-%s-%s>' % (self.year, self.mon, self.day)) class OldboyPeople: school = 'oldboy' def __init__(self, name, age, sex): self.name = name self.age = age self.sex = sex class OldboyTeacher(OldboyPeople): def __init__(self,name,age,sex,level,salary): super().__init__(name,age,sex) #重用父类功能 self.level=level self.salary=salary def change_score(self): print('teacher %s is changing score' %self.name) class Oldboystudent(OldboyPeople): def __init__(self,name,age,sex,course,): super().__init__(name,age,sex,) #重用父类功能 self.course=course def choose(self): print('student %s choose course' %self.name) tea1=OldboyTeacher('egon',18,'male',9,3.1) #创建老师类的对象tea1 date_obj=Date(2000,1,1) #创建Date类的对象date_obj date_obj.tell_birth() #date_obj可以调用绑定方法tell_birth tea1.birth=date_obj #tea1的birth属性来自于Date类的一个对象date_obj tea1.birth.tell_birth() #tea1的birth属性可以调用tell_birth属性 stu1=Oldboystudent('张三',16,'male','linux') stu1.birth=Date(2002,3,3) stu1.birth.tell_birth() stu1.choose() #使用stu1将两个类联系起来
7.5 封装
什么是封装: 装就是把一堆属性存起来,封的概念就把这些属性给隐藏起来,其实这种隐藏只是一种语法上的变形,对外不对内
注意:
为一个属性名加__开头
(注意不要加__结尾
),会在类定义阶段将属性名统一变形:_自己的类名__属性名
class Foo: x=1 __x=1111 #_Foo__x=1111 def __init__(self,y): self.__y=y #self._Foo__y=y def __f1(self): #_Foo__f1 print('Foo.f1') def get_y(self): print(self.__y) # print(self._Foo__y) obj=Foo(22222) print(obj.x) # 1 print(obj.__x) # 报错 print(obj._Foo__x) # 1111 obj.__f1() #报错 obj._Foo__f1() # Foo.f1 print(obj.y) #报错 print(obj.__y) #报错 print(obj._Foo__y) # 22222 obj.get_y() # 22222 明确地区分内外,对外是隐藏的,对内是开放的
这种语法意义上变形,只在类定义阶段发生一次,类定义之后,新增的__开头的属性都没有变形的效果
Foo.__aaa=1
print(obj.__aaa) # 1
如果父类不想让子类覆盖自己的方法,可以在方法名前加__开头
class Foo: def __f1(self): #_Foo__f1 print('Foo.f1') def f2(self): print('Foo.f2') self.__f1() #obj._Foo__f1() class Bar(Foo): def __f1(self): #_Bar__f1 print("Bar.f1") obj=Bar() obj.f2() # Foo.f2 Foo.f1
7.51 封装的作用
封装数据属性的目的:
把数据属性封装起来,然后需要开辟接口给类外部的使用者使用,好处是我们可以在接口之上添加控制逻辑,从而严格空间访问者对属性的操作
class People: def __init__(self,name): self.__name=name def tell_name(self): # 添加逻辑 return self.__name obj=People('egon') #obj.__name obj.tell_name()
封装函数属性的目的:为了隔离复杂度
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() obj=ATM() obj.withdraw()
7.52 封装之property
用来将类内的函数伪装成一个数据属性
例:
体质指数()体重()身高()
首先需要明确 : bmi是算出来的,不是一个固定死的值,也就说我们必须编写一个功能,每次调用该功能 都会立即计算一个值
class People: def __init__(self,name,weight,height): self.name=name self.weight=weight self.height=height @property #于是我们需要为bmi这个函数添加装饰器,将其伪装成一个数据属性 def bmi(self): return self.weight / (self.height * self.height) egon=People('egon',75,1.80) yl=People('yangli',85,1.74) # print(egon.bmi()) # print(yl.bmi()) print(egon.bmi) # 21.604938271604937,调用egon.bmi本质就是触发函数bmi的执行,从而拿到其返回值 print(yl.bmi) # 把功能伪装成一个属性
@name.setter 和 @name.deleter
# egon.bmi=123 # egon.bmi背后对应的是一个函数,所以不能赋值 class People: def __init__(self,name): self.__name=name @property def name(self): # 添加逻辑 return self.__name @name.setter def name(self,x): # 添加逻辑 self.__name=x @name.deleter def name(self): # 添加逻辑 del self.__name obj=People('egon') print(obj.name) # egon # 修改 obj.name='EGON' # 现在可以赋值 print(obj.name) # EGON del obj.name # 删除 obj.name # 报错