面向对象之组合、封装、多态、property装饰器
概要:
1 Python推崇鸭子类型:解耦合,统一标准(不用继承) 2 1. 组合 3 继承:会传递给子类强制属性 4 组合:解耦合,减少占用内存.如:正常继承,如果一个班级有100个学生,那么这个学生的重复的课程信息会存一百遍,浪费内存 5 6 一个对象有一个属性,该属性的值是来自于另外一个类的对象 7 8 2. 封装 9 装指的是将属性装进一个容器 10 封指的是将容器内的属性隐藏起来 11 12 如何做到"封":在属性名前加上__开头,特点如下: 13 1. __开头的属性会在检测语法时发生变形,规则_类名__属性名 14 2. 该隐藏是对外不对内的 15 3. 该变形只在语法检测时发生一次,在类定义之后新增的__开头的属性不会发生变形 16 4. 父类不想让子类覆盖自己的属性,可以在属性前加上__开头 17 18 封装不是单纯意义的隐藏: 19 1. 封装数据属性: 是为了开放接口让类外部的使用者间接地去操作属性,从而我们可以通过修改接口的逻辑来严格控制使用者对属性的操作 20 2. 封装函数属性: 隔离复杂度 21 3. property 22 是一个装饰器,用来将类中定义的函数伪装成一个数据属性 23 class Foo: 24 @property 25 def xxx(self): # 查看属性的功能 26 pass 27 28 @xxx.setter 29 def xxx(self,val): # 修改属性的功能 30 pass 31 32 @xxx.deleter 33 def xxx(self): # 删除属性的功能 34 pass 35 原始方式: 36 def c(self): 37 pass 38 def a(self): 39 pass 40 def b(self): 41 pass 42 43 xxx=property(a,b,c) 44 4. 多态 45 多态指的是同一种事物的多种形态 46 多态性指的是可以在不用考虑对象具体类型的前提下而直接使用对象 47 继承父类,肯定继承了父类里的功能
组合:
1. 什么是组合 一个对象的属性是来自于另外一个类的对象,称之为组合
此时,这个对象就获得了另一个对象的所有属性和功能 2. 为何用组合 组合也是用来解决类与类代码冗余的问题
3. 如何用组合
演示:
1 class Foo: 2 aaa=1111 3 def __init__(self,x,y): 4 self.x=x 5 self.y=y 6 7 def func1(self): 8 print('Foo内的功能') 9 10 class Bar: 11 bbb=2222 12 def __init__(self, m, n): 13 self.m = m 14 self.n = n 15 16 def func2(self): 17 print('Bar内的功能') 18 19 obj1=Foo(10,20) 20 obj2=Bar(30,40) 21 22 obj1.xxx=obj2 23 #此行代码就是组合的过程 24 print(obj1.x,obj1.y,obj1.aaa,obj1.func1) 25 print(obj1.xxx.m,obj1.xxx.n,obj1.xxx.bbb,obj1.xxx.func2)#组合之后就可以利用obj1.xxx来调用对象obj2所有的功能
组合的优点及其演化过程:
第一步:最顶级的类的数据属性会传递给依靠类生成的所有对象
1 class OldboyPeople: 2 school = 'Oldboy' 3 # 如果有一个管理员的话,也被迫添加了课程相关的属性 4 def __init__(self, name, age, gender, course_name, course_price, course_period): 5 self.name = name 6 self.age = age 7 self.gender = gender 8 self.course_name = course_name 9 self.course_price = course_price 10 self.course_period = course_period 11 12 class OldboyStudent(OldboyPeople): 13 def choose_course(self): 14 print('%s is choosing course' % self.name) 15 16 class OldboyTeacher(OldboyPeople): 17 def __init__(self, name, age, gender, level, salary, course_name, course_price, course_period): 18 OldboyPeople.__init__(self, name, age, gender) 19 self.level = level 20 self.salary = salary 21 22 def score(self, stu, num): 23 stu.num = num 24 print('老师%s给学生%s打分%s' % (self.name, stu.name, num)) 25 #对于课程名,价格,周期等不需要重复传入,会增加内存占用 26 stu1 = OldboyStudent('egon', 18, 'male', 'Python开发', 3000, '5mons') 27 stu2 = OldboyStudent('kevin', 38, 'male', 'Python开发', 3000, '5mons') 28 #定义课程函数 29 def tell_course(obj): 30 print('课程名:<%s> 价钱:[%s] 周期:[%s]' % (obj.course_name, obj.course_price, obj.course_period)) 31 32 tell_course(stu1) 33 tell_course(stu2)
第二步
1 class OldboyPeople: 2 school = 'Oldboy' 3 def __init__(self, name, age, gender): 4 self.name = name 5 self.age = age 6 self.gender = gender 7 8 class OldboyStudent(OldboyPeople): 9 def choose_course(self): 10 print('%s is choosing course' %self.name) 11 12 class OldboyTeacher(OldboyPeople): 13 def __init__(self, name, age, gender,level,salary): 14 OldboyPeople.__init__(self, name, age, gender) 15 self.level=level 16 self.salary=salary 17 18 def score(self,stu,num): 19 stu.num=num 20 print('老师%s给学生%s打分%s' %(self.name,stu.name,num)) 21 #定义课程类 22 class Course: 23 def __init__(self,course_name,course_price,course_period): 24 self.course_name=course_name 25 self.course_price=course_price 26 self.course_period=course_period 28 def tell_course(self): 29 print('课程名:<%s> 价钱:[%s] 周期:[%s]' % (self.course_name, self.course_price, self.course_period)) 30 31 python_obj=Course('python开发',3000,'5mons') 32 linux_obj=Course('linux运维',5000,'3mons')
stu1=OldboyStudent('egon',18,'male')
stu1.courses=[]#给学生对象添加课程属性
stu1.courses.append(linux_obj)#组合(追加课程类对象),解决了代码冗余,浪费内存,解耦合(避免父类的所有属性传递给子类)
stu1.courses.append(python_obj)
stu1.courses[0].tell_course()#调用课程类中的查课方法
封装(__属性):
1. 什么是封装 装指的是把属性装进一个容器 封指的是隐藏的意思,但是这种隐藏式对外不对内的 2. 为何要封装 封装不是单纯意义的隐藏 封装数据属性的目的:将数据属性封装起来,类外部的使用就无法直接操作该数据属性了 需要类内部开一个接口给使用者,类的设计者可以在接口之上附加任意逻辑,从而严格控制使用者对属性的操作 封装函数属性的目的:隔离复杂度 3. 如何封装
在python中用双下划线开头的方式将属性隐藏起来(设置成私有的) 只需要在属性前加上__开头,该属性就会被隐藏起来,该隐藏具备的特点: 1. 只是一种语法意义上的变形,即__开头的属性会在检测语法时发生变形_类名__属性名 2. 这种隐藏式对外不对内的,因为在类内部检测语法时所有的代码统一都发生的变形 3. 这种变形只在检测语法时发生一次,在类定义之后新增的__开头的属性并不会发生变形 4. 如果父类不想让子类覆盖自己的属性,可以在属性前加__开头
示例:
1 class Foo: 2 __x=111 #_Foo__x = 111 3 def __init__(self,m,n): 4 self.__m=m # self._Foo__m=m 5 self.n=n 6 7 def __func(self): #_Foo__func 8 print('Foo.func') 9 10 def func1(self): 11 print(self.__m) #self._Foo__m 12 print(self.__x) #self._Foo__x 13 14 print(Foo.__dict__) 15 # Foo.__x 报错,属性被隐藏,找不带属性 16 # Foo.__func 17 # print(Foo._Foo__x) 18 # print(Foo._Foo__func)
obj=Foo(10,20)
print(obj.__dict__)#{'_Foo__m': 10, 'n': 20}
print(obj.n)#20
# print(obj.__m)#报错,找不到
print(obj._Foo__m)#10
obj.func1()#10,111,通过调用内部的方法,内部方法可以访问被封装的数据
容易犯错:
#要区分没有被封装的类中的属性查找 class Foo: def __f1(self): #_Foo__f1 print('Foo.f1') def f2(self): print('Foo.f2') self.__f1() #self._Foo__f1 class Bar(Foo): def __f1(self): #_Bar__f1 print('Bar.f1') obj=Bar() obj.f2()#Foo.f2 Foo.f1
#封装之后在检测语法的时候会进行语法变形。属性查找按照变形之后的名字
封装函数属性的真实意图:隔离复杂度
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()#讲取款的复杂流程封装为一个简单的步骤
-------
class People: def __init__(self,name,age): self.__name=name self.__age=age def tell_info(self): print('<name:%s age:%s>' %(self.__name,self.__age)) def set_info(self,new_name,new_age): if type(new_name) is not str: print('名字必须是str类型傻叉') return if type(new_age) is not int: print('年龄必须是int类型傻叉') return self.__name=new_name self.__age=new_age print('<name:%s age:%s>' % (self.__name, self.__age)) def clear_info(self): del self.__name del self.__age obj=People('egon',18) obj.tell_info()#通过一个功能直接完成要做的事 obj.set_info('alex',78)
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) obj=People('egon',70,1.82) obj.height=1.85 print(obj.bmi)#bmi在类中属于一个功能,property将其装饰成一个属性
property的用法:查,改,删
1 class People: 2 def __init__(self,name): 3 self.__name=name 4 5 @property 6 def name(self):#查 7 return '<name:%s>' %self.__name 8 9 @name.setter 10 def name(self,new_name):#改 11 if type(new_name) is not str: 12 print('名字必须是str类型') 13 return 14 self.__name=new_name 15 16 @name.deleter 17 def name(self):#删 18 del self.__name 19 20 obj=People('egon') 21 # 查 22 print(obj.name) 23 # 改 24 obj.name='alex' 25 print(obj.name) 26 # 删 27 del obj.name 28 print(obj.__dict__)
--------------
1 class People: 2 def __init__(self,name): 3 self.__name=name 4 # 查 5 def xxx_name(self): 6 return '<name:%s>' %self.__name 7 # 改 8 def yyy_name(self,new_name): 9 if type(new_name) is not str: 10 print('名字必须是str类型') 11 return 12 self.__name=new_name 13 # 删 14 def zzz_name(self): 15 del self.__name 16 # 必须按照查,改,删的顺序 17 name=property(xxx_name,yyy_name,zzz_name) 18 19 obj=People('egon') 20 print(obj.name)#<name:egon> 21 22 obj.name=123 23 print(obj.name)#名字必须是str类型 24 25 del obj.name 26 print(obj.__dict__)#{}
多态:
1. 什么是多态 同一种事物的多种形态 2. 为何要用多态 多态性:指的是可以在不用考虑对象具体类型的前提下而直接使用对象下的方法 3. 如何用多态
多态示例:
1 import abc 2 class Animal(metaclass=abc.ABCMeta): 3 # 抽象方法 4 @abc.abstractmethod 5 def speak(self): 6 pass 7 # Animal() # 这里父类不能实例化,因为父类本身就是用来制定标准的 8 class People(Animal): 9 def speak(self): 10 print('say hello') 11 # def jiao(self): 12 # print('say hello') 13 class Dog(Animal): 14 def speak(self): 15 print('汪汪汪') 16 class Pig(Animal): 17 def speak(self): 18 print('哼哼哼') 19 20 peo=People() 21 dog1=Dog() 22 pig1=Pig() 23 24 peo.speak() 25 dog1.speak() 26 pig1.speak()
代码转换:
''.__len__() [].__len__() len([1,2,3]) #[1,2,3].__len__()
1 #方式一 2 peo=People() 3 dog1=Dog() 4 pig1=Pig() 5 6 peo.speak() 7 dog1.speak() 8 pig1.speak() 9 #方式二 10 def speak(animal): 11 animal.speak() 12 13 speak(peo) 14 speak(dog1) 15 speak(pig1)
Linux系统任何操作都是基于文件(均是对文件的读写操作):
1 class Memory: 2 def read(self): 3 print('mem read') 5 def write(self): 6 print('mem write') 7 8 class Disk: 9 def read(self): 10 print('disk read') 12 def write(self): 13 print('disk write') 14 15 class Cpu: 16 def read(self): 17 print('cpu read') 19 def write(self): 20 print('cpu write') 21 22 obj1=Memory() 23 obj2=Disk() 24 obj3=Cpu() 25 26 obj1.read() 27 obj2.read() 28 obj3.read()