一. 什么是组合
对象的属性是另一个对象或者可以认为一个对象是另一个对象属性
为什么使用组合?===> 减少代码的冗余
class Person: school = 'oldboy' class Teacher(Person): def __init__(self,name,age,level,course): self.name=name self.age=age self.level=level #course是课程对象,表示老师教授的课程 self.course=course class Student(Person): def __init__(self,name,age,course): self.name=name self.age=age # course是课程对象,表示学生选的课程 self.course = course class Course: def __init__(self,course_name,course_price,course_period): self.name=course_name self.price=course_price self.period=course_period
之前如果没有没有Course这个类,那学生和老师的数据属性中都有course_name,course_price,course_period 这三个属性,为了减少代码的冗余,我们再重新加一个Course类,那么老师和学生就可以将这个course来当做自己的属性来调用
什么时候用组合什么时候用继承呢, 当什么是什么的关系用继承, 什么有什么的关系用组合
二. 多态
什么是多态? 多态就是一类事物的多种形态,动物有多种形态,比如人, 狗, 猪
class Animal: def speak(self): pass class Pig(Animal): def speak(self) print('哼哼哼') class Dog(Animal): def speak(self) print('汪汪汪') class People(Animal): def speak(self): print('say hello') pig = Pig() dog = Dog() people = Pepple() pig.speak() dog.speak() people.speak() # 如果按上面这种方法调用的话程序有点冗长,我们 可以先定义一个调用speak的函数,因为上面三个类中有共同的speak方法, def Animal_speak(obj) obj.speak() Animal_speak(pig) Animal_speak(dog) Animal_sspeak(people)
现在假如有这么一种情况,当人, 猪, 狗叫的表示方法不同时,怎么办呢?这里呀,我们程序员约定俗称的可以借用模块abc,来实现统一化看下面例子:
# 用abc来实现接口的统一化,来约束代码,只要是我定义了speak这个方法,你只要是继承我,那你就得用speak这个方法,如果不用就会报错,通过这样的约束来完成多态,只要是这一类的方法,都可以进行调用,多态其实就是用来约束我子类该用什么方法 import abc class Animal(metaclass=abc.ABCMeta): @ abc.abstractmethod def speak(self): pass class Pig(Animal): def xxx(self) print('哼哼哼') class Dog(Animal): def yyy(self) print('汪汪汪') class People(Animal): def zzz(self): print('say hello')
在Python中崇尚鸭子类型(只要走路像鸭子,(对象中有某个绑定方法),那你就是鸭子),么有什么东西可以约束我子类方法应该怎么去写,都是人为规定的,不规定想怎么写就怎么写(鸭子类型不需要继承)
3. 封装
什么是封装?从封装本身的意思去理解,封装就好像是拿来一个麻袋,把小狗,小猫一起装进麻袋,然后把麻袋口子封上
如何隐藏, 把东西包进去之后,隐藏起来,外部访问不到
如何用代码实现隐藏? 隐藏属性和隐藏方法 隐藏之后只有内部才能访问,外部访问不到
隐藏属性: 通过__变量名来隐藏
隐藏方法: 通过__方法名来隐藏
# 将name 隐藏起来 class Person: def __init__(self, name, age) self.__name = name self.age = age
def get_name(self) # 指的是内部
return self.__name
p = Person('panshao', 18) print(p.age) # 18 print(p.name) # 抛出错误 指的是外部
print(p.get_name()) # 正常打印出名字
通过people.__dict__查看属性可以看到下面结果
通过上面的图可以清楚的看到隐藏的本质就是通过变形才隐藏了属性,我们试着打印下people._People__name,结果还真得到了名字.
封装属性是防止别人乱改,保证安全性,封装方法是隔离复杂度
什么时候属性变形了呢? 只要在类内部,以__变量名来命名的变量,都会被隐藏,会发生变形,在外部放入的__变量名是不进行隐藏的
class Person: def __init__(self, name, weight, height) self.name = name self.weight = weight self.height = height def bmi(self) return self.weight/(self.height**2) p = Person('bgon', 80, 1.78) print(p.bmi())
我们可以通过p.bmi()来获取bmi的值, 但是仔细想一下,因为人的体重是变的,bmi肯定也会跟着会变,这里可以用@property装饰器,他可以将方法包装成数据属性,在调用bmi的时候不用加括号,但是这样还是有错误的,就是说p.bmi在外部不能重新被修改
property之setter和deleter class Person: def __init__(self,name,height,weight): self.__name=name self.__height=height self.__weight=weight @property def name(self): return '[我的名字是:%s]'%self.__name #用property装饰的方法名.setter @name.setter def name(self,new_name): # if not isinstance(new_name,str): if type(new_name) is not str: raise Exception('改不了') if new_name.startswith('sb'): raise Exception('不能以sb开头') self.__name=new_name # 用property装饰的方法名.deleter @name.deleter def name(self): # raise Exception('不能删') print('删除成功') # del self.__name p=Person('lqz',1.82,70) # print(p.name) # p.name='pppp' # p.name='xxx' #改不了,直接抛一异常 # p.name=999 # p.name='sb_nick' # 这两种方法都改不了 # print(p.name) del p.name print(p.name)
四. 封装的扩展性
#类的设计者 class Room: def __init__(self,name,owner,width,length,high): self.name=name self.owner=owner self.__width=width self.__length=length self.__high=high def tell_area(self): #对外提供的接口,隐藏了内部的实现细节,此时我们想求的是面积 return self.__width * self.__length #使用者 >>> r1=Room('卧室','egon',20,20,20) >>> r1.tell_area() #使用者调用接口tell_area 400 #类的设计者,轻松的扩展了功能,而类的使用者完全不需要改变自己的代码 class Room: def __init__(self,name,owner,width,length,high): self.name=name self.owner=owner self.__width=width self.__length=length self.__high=high def tell_area(self): #对外提供的接口,隐藏内部实现,此时我们想求的是体积,内部逻辑变了,只需求修该下列一行就可以很简答的实现,而且外部调用感知不到,仍然使用该方法,但是功能已经变了 return self.__width * self.__length * self.__high #对于仍然在使用tell_area接口的人来说,根本无需改动自己的代码,就可以用上新功能 >>> r1.tell_area() 8000