组合
1.什么是组合
在一个类中以另外一个类的对象作为数据属性,称为类的组合。
继承是一个子类是一个父类的关系,而组合则是一个类有另一个类的关系。
组合就是一个类中使用到另一个类,从而把几个类拼到一起。组合的功能也是为了减少重复代码。
2.为什么要使用组合
组合的目的和继承一样,为了减少代码冗余
3.如何使用组合
# 定义父类
class people:
def __init__(self,name,age):
self.name = name
self.age =age
# 定义学生类
class teach(people):
def __init__(self,name,age):
super().__init__(name,age)
# 定义老师类
class student(people):
def __init__(self,name,age):
super().__init__(name,age)
# 定义一个日期类
class Data:
def __init__(self,n,y,d):
self.n = n
self.y = y
self.d = d
def dayin(self):
print(f'''
===打印年月日===
年:{self.n}
月:{self.y}
日:{self.d}
''')
# 实例化teach得到对象t
t = teach('aaa','s')
# 实例化Data 得到data_obj对象
data_obj = Data(1999,10,1)
# 将data_obj添加到对象t中,让t 可以调用Data的方法
t.data = data_obj
# t中有data_obj == t中可以使用Data的方法
t.data.dayin()
组合的练习
'''
选课系统需求:
1.学生类,老师类, 学生和老师都有课程属性, 每一门课程都是一个对象.
课程: 课程名字,课程周期,课程价钱
2.学生和老师都有选择课程的功能, 还有打印所有课程的功能.
'''
# 定义父类
class people:
def __init__(self,name,age,sex):
self.name = name
self.age =age
self.sex = sex
'''定义选课的方法'''
def add_kecheng(self,course_obj):
'''将选择的课程传入'''
self.course_list.append(course_obj) # self自己的list列表增加传入的课程
'''得到一个老师学生的对象'''
def print_all(self): # (传入当前的老师或学生对象)
'''循环拿到当前对象的课程列表,列表中存放的是一个个的课程对象'''
for course_obj in self.course_list: # 对象的课程列表(之前追加的课程)
'''循环每一个课程对象调用查看课程信息的方法'''
course_obj.get_info() # 获取到的课程对象直接调用其中的打印方法
# 定义老师类
class teach(people):
def __init__(self,name,age,sex):
super().__init__(name,age,sex)
# 学生选课的功能
self.course_list = []
# 定义学生类
class student(people):
def __init__(self,name,age,sex):
super().__init__(name,age,sex)
self.course_list = []
# 定义一个课程类
class Course:
'''定义课程类的名称周期价格'''
def __init__(self,course_name,course_period,course_prize):
self.course_name = course_name
self.course_period = course_period
self.course_prize = course_prize
def get_info(self):
'''打印课程的所有信息'''
print(f'''
课程名称:{self.course_name}
课程周期:{self.course_period}
课程价格:{self.course_prize}
''')
# 实例化老师与学生的对象
tea1 = teach('老师',10,'男性')
stu1 = student('学生',50,'男性')
# 实例化课程的对象
python_obj = Course('python0',6,2.0)
linux_obj = Course('linux',6,1.0)
# 通过老师学生将两门课程对象添加进teach内
'''将整个课程信息实例化的对象传入了add_kecheng方法中
教师类中列表course_list接受课程信息'''
tea1.add_kecheng(python_obj)
tea1.add_kecheng(linux_obj)
'''将保存在tea1中的python获取并使用打印方法'''
# tea1.course_list[0].get_info()
# python_obj.get_info()
'''直接调用查看所有的课程'''
tea1.print_all()
总结
继承:
类与类的关系,一种什么是什么的关系,子类与父类是从属关系
组合:
对象与对象的关系,一种什么有什么的关系,一个对象拥有另一个对象
封装
1.什么是封装
把一堆属性(特征与技能)封装到一个对象中,对象可以通过.
的方式获取属性
这就是面向对象的第一个特性(好处)
:封装
封装特性,可以把复杂的信息,流程,包起来,内部处理,让使用者不去关注细节
2.为什么要封装
目的是为了方便存取,可以通过对象.
属性的方式获取属性.
3.如何封装
特征 : 变量 ==> 数据类型
技能 : 函数 ==>方法属性
在类内部定义一堆属性(特征与技能)
通过 对象.属性 = 属性值
方式
访问限制机制
在Python中,实例的变量名如果以__
开头,就变成了一个私有变量(private),只有内部可以访问,外部不能访问
1.什么是访问限制机制
在类内部定义,凡是以__
开头的数据属性与方法属性
都会被python内部隐藏起来,让外部不能直接访问类内部__
开头的属性.
比如 __name = 'tank'
2.访问限制的目的
一堆隐私的属性与不能被外部轻易访问的属性,可以隐藏起来,不被外部直接调用
好处: 对重要数据获取的逻辑更加严谨,进而保证了数据的安全.
class Foo:
# 数据类型
__name = 'tank'
# 方法属性
def __run(self):
print('runing...')
foo = Foo()
print(Foo.__name)
'''报错type object 'Foo' has no attribute '__name''''
接口
隐私属性可以挺过封装一个接口,在借口内做业务逻辑的处理,再把数据返回给调用者
# 类在定义时执行,使用接口,隐私
class Foo:
# 数据类型
__name = 'tank'
# 方法属性
def __run(self):
print('runing...')
# 接口
def get_name(self):
'''在接口里返回出去,类内部访问'''
# 可以做些业务逻辑判读是否给你访问
return self.__name
foo = Foo()
res = foo.get_name()
print(res)
# tank
'''内部修改'''
def set_name(self):
self.__name = 'nick'
foo = Foo()
foo.set_name()
print(foo.get_name())
# nick
'''访问限制判断'''
class ATM:
# 1.插卡
def __inster_card(self):
print('插卡')
pass
# 2.输入密码
def __input_pwd(self):
print('输入密码')
pass
# 3.输入取款
def __money(self):
print('输入金额')
pass
# 4.开始吐钱
def __getmoney(self):
print('开始吐钱')
pass
# 5.打印账单
def flow(self):
print('流水')
pass
# 取钱直接接口,封装所有的隐藏接口
def withdraw(self):
self.__inster_card()
self.__input_pwd()
self.__money()
self.__getmoney()
self.flow()
# 实例化atm
atm = ATM()
atm.withdraw()
注意: 在python中,不会强制限制属性的访问,类内部__
开头的属性,只是做了一种变形
class Foo:
__name = 'tank'
# 将__name改成 _student__name了
print(foo._Foo__name)
# tank
_(父类)__(属性名) 可以直接获取
property
1.什么是property
python中内置的装饰器,主要是给类内部的方法使用
2.为什么要用property
将类内部的方法def 方法名()
变成def 方法
,
在对象调用某个方法是将对象.方法()
变成对象.方法
,看着像数据属性
3.如何使用property
@ property
注意 不能对呗装饰过的方法属性修改
'''
计算人体bmi, bmi = 体重/身高的平方
'''
class People:
def __init__(self,name,w,h):
self.name = name
self.w = w
self.h = h
def bmi(self):
return self.w / (self.h ** 2 )
t1 = People('wang',160,175)
print( t1.bmi() )
# 加()像是动词,所以使用property 让其变为名词
'''使用 @ property '''
class People:
def __init__(self,name,w,h):
self.name = name
self.w = w
self.h = h
@property # 在想要变成普通的函数上进行调用
def bmi(self):
return self.w / (self.h ** 2 )
t1 = People('wang',160,175)
print( t1.bmi ) # 打印时去除括号()
'''不能对被装饰过的方法进行属性修改'''
t1.bmi = 18
# 会进行报错
了解: 可以通过特殊方法来进行修改(通过修改类中属性)
# 改
class People:
def __init__(self, name, w, h):
self.name = name
self.w = w
self.h = h
@property # 在想要变成普通的函数上进行调用
def bmi(self):
return self.w / (self.h ** 2)
@property
def get_name(self):
return self.name
@get_name.setter
def set_name(self, val):
self.name = val
t1 = People('wang', 160, 175)
print(t1.get_name)
t1.set_name = 'tank'
print(t1.set_name)
'''wang
tank'''
多态
1.什么是多态
多态指的是同一种事物的多种形态,
就是同样的行为,后代们有多种不同的状态
多态就是 从父亲那里继承来的同一个的行为, 孩子们各自的表现状态不一样
2.多态的目的
多态也称之为多态性,在程序中继承就是多态的表现形式
多态的目的是为了, 让多种不同类型的对象, 在使用相同功能的情况下,调用同一个名字的方法名.
父类: 定义一套统一的标准
子类: 遵循父类统一的标准
多态的最终目的: 统一子类编写的规范,为了让使用者更方便调用相同功能的方法
3.如何实现
1.继承父类
在python中不会强制要求子类必须遵循父类的一套标准,所以出现了抽象类
2.抽象类abc
1.是什么:
abc模块 abstract_class
2.使用的目的
强制子类必须遵循父类的一套标准
3.如何使用
import abc
import abc
class animal(metaclass=abc.ABCMeta):
@abc.abstractclassmethod
def eat(self):
pass
@abc.abstractclassmethod
def drink(self):
pass
@abc.abstractclassmethod
def speak(self):
pass
class pig(animal):
def eat(self):
pass
def speak(self):
pass
def drnk(self):
pass
p = pig()
# 只要继承了父类,就必须有父类的方法,可以多不能少
3.鸭子类型
1.什么是鸭子类型
鸭子类型是编程语言中动态类型语言中的一种设计风格,一个对象的特征不是由父类决定,而是通过对象的方法决定的
Python 不检查传入的对象的类型,这种方式通俗的被称为「鸭子类型」,比较高端的方式是叫做「隐式类型」或者「结构式类型」。
==例如迭代器,我们并不需要继承Iterable或者Iterator,只需要实现__iter__ 和 __next__方法的对象都可称之为迭代器,本身可以为任何类==
在不知道当前对象是何物的情况下,但是你长得像鸭子,那么你就是鸭子类型
在python中,不推荐使用抽象类强制限制子类的定义,但推荐类都遵循鸭子类型
鸭子类型就意味着可以向任何对象发送任何的消息,语言只关心这个对象能不能接收该消息,不会去强求该对象是否为某一种特定的类型 —— 该对象的多态表现。
代码
class Animal(object):
def run(self):
print("The animal is running...")
class Dog(Animal):
def run(self):
print('The dog is running...')
class Cat(Animal):
def run(self):
print('The cat is running...')
# 定义一个函数,只需要保证传入的有一个.run方法即可
def makeRun(animalType):
animalType.run()
dog = Dog()
cat = Cat()
makeRun(dog)
makeRun(cat)
'''
The dog is running...
The cat is running...
'''
我们可以使用一个函数 makeRun()
来访问不同 Animal
子类中的相同方法。但其实对于上面的 makeRun()
函数来说,传入的参数并不一定需要是 Animal
类型的,只需要保证传入的对象有一个 run()
方法即可,如下面代码所示。这就是动态语言的“鸭子类型”,它并不要求严格的继承体系,一个对象只要“看起来像鸭子,走起路来像鸭子”,那它就可以被看做是鸭子。
class Person:
def run(self):
print("The person is running...")
person = Person()
makeRun(person)
# The person is running...
而在静态语言中,如 Java ,如果需要传入 Animal
类型,则传入的对象就必须是 Animal
类型或者它的子类,否则,将无法调用 run()
方法。
-继承
耦合性太高,程序的可扩展性差
-鸭子类型
耦合性低,程序的可扩展性强