【0828 | Day 25】类的组合/多态与多态性/封装
目录
组合
一、什么是组合
对象的某个属性是另外定义的一个类的对象
#举个栗子
class Animal:
def __init__(self, level):
self.level = level
class Level:
pass
#Animal类的属性level是另一个类的对象
level = Level()
l = Animal(level)
二、为什么使用组合
-
减少代码冗余
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('Python', 20000, 6) stu=Student('nick', 19, course) teacher=Teacher('nick', 19, '高级', course) #查看老师教授的课程名 print(teacher.course.name) #Python
三、如何使用组合
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):
# course=[] #错误
def __init__(self, name, age):
self.name = name
self.age = age
# course是课程对象,表示学生选的课程
self.course_list = []
def choose_course(self, course):
# self.course=[] #错误
#把课程对象追加到学生选课的列表中
self.course_list.append(course)
def tell_all_course(self):
#循环学生选课列表,每次拿出一个课程对象
for course in self.course_list:
#课程对象.name 取到课程名字
print(course.name)
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('Python',20199,7)
stu1 = Student('nick',19)
stu1.choose_course(course)
stu2 = Student('王二丫',19)
stu2.choose_course(course)
stu2.choose_course(Course('linux',19999,5))
#查看stu1选的所有课程名称
#方式一(通过普通函数)
def tell_all_course(student):
for course in student.course_list:
print(course.name)
tell_all_course(stu1)
tell_all_course(stu2)
#方式二(通过对象的绑定方法)
stu1.tell_all_course()
stu2.tell_all_course()
多态和多态性
一、什么是多态
表示一类事物具有多种形态,例如class Animal,这个Animal类里又分为人、猪、猫等等
二、什么是多态性
表示向不同的对象发送同一条消息,不同的对象在接收时会产生不同的行为(即方法)
class Animal:
def run(self): # 子类约定俗称的必须实现这个方法 #def click(self):....等
raise AttributeError('子类必须实现这个方法')
class People(Animal):
def run(self):
print('人正在走')
class Pig(Animal):
def run(self):
print('pig is walking')
class Dog(Animal):
def run(self):
print('dog is running')
peo1 = People()
pig1 = Pig()
d1 = Dog()
# 多态性:一种调用方式,不同的执行效果(多态性)
def func(obj):
obj.run()
#obj.click()
#ocj.bite()
...
func(peo1)
func(pig1)
func(d1)
#人正在走
#pig is walking
#dog is running
总结:多态性是一个接口(函数func)的多种实现(如obj.run(),obj.talk(),obj.click(),len(obj))
三、多态性的优点
- 增加了程序的灵活性:以不变应万变,不论对象千变万化,使用者都是同一种形式去调用,如func(animal)
- 增加了程序额可扩展性:通过继承Animal类创建了一个新的类,使用者无需更改自己的代码,还是用func(animal)去调用
class Cat(Animal): # 属于动物的另外一种形态:猫
def talk(self):
print('say miao')
def func(animal): # 对于使用者来说,自己的代码根本无需改动
animal.talk()
cat1 = Cat() # 实例出一只猫
func(cat1) # 甚至连调用方式也无需改变,就能调用猫的talk功能
四、鸭子类型
可以用一个函数去调用任意定义的类中的方法,例如:
#崇尚鸭子类型:只要走路像鸭子(对象中有某个绑定方法),那你就是鸭子
class Pig:
def speak(self):
print('哼哼哼')
class People:
def speak(self):
print('say hello')
pig=Pig()
pe=People()
def animal_speak(obj): #同一函数调用
obj.speak()
animal_speak(pig)
animal_speak(pe)
封装
一、什么是封装
封装即将许多东西装进麻袋,然后封口隐藏,使得外部无法访问。
二、如何隐藏属性/方法
-
隐藏属性:通过__变量名来隐藏(可以内部调用)
- 更安全
class Person: def __init__(self, name): self.__name = name def check_name(self): return '%s' %self.__name #可以内部调用 p = Person('nick') #访问name print(p.name) print(p.__name) #以上两种无法获取,已被隐藏。 print(p.get_name()) #可通过函数内部调用获取 #获取方法 ①--> print(p.__dict__) #获取隐藏名 ②--> print(p._Person__name)
-
隐藏方法:通过__方法名来隐藏
- 隔离复杂度
class Person: def __init__(self,name,age): self.__name = name self.__age = age def __speak(self): print('6666') p=Person('nick',89) #p.__speak() 无法调用 #获取方法 ①--> print(Person.__dict__) ②--> p._Person__speak()
注意:在类内部以__变量名命名的变量,都会被隐藏。在外部放入的__变量名,属性不隐藏。
举个栗子:
class Person: def __init__(self,name,age): self.__name = name self.__age = age def set_xx(self,xx): self.__xx = xx print(self.__xx) p=Person('nick',18) p.__level = 5 #在类外__变量名放入新变量,不会隐藏属性 p.set_xx('6688')
property装饰器
一、什么是property
将方法包装成数据属性
#计算人的bmi指数
#property装饰器:把方法包装成数据属性
class Person:
def __init__(self, name, height, weight):
self.name = name
self.height = height
self.weight = weight
@property
def bmi(self):
return self.weight/(self.height**2)
p = Person('lqz', 1.82, 70)
# print(p.bmi()) #用了@property,不需要再加括号调用
print(p.bmi)
p.name='ppp'
print(p.name) #ppp
p.bmi=90
print(p.bmi) #报错,因为此时的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)