目录
组合
什么是组合
- 一个对象中的属性是另一个类
为什么要组合
- 减少代码冗余并且可以提高程序可扩展性
如何使用组合
-
将一个类实例出的对象当做属性添加给另一个类实例出的对象
class People: def __init__(self,name): self.name = name class Date: def __init__(self,year,month,day): self.year = year self.month = month self.day = day def tell_birth(self,obj): print( f''' ==={obj.name}的出生日期=== 年 {self.year} 月 {self.month} 日 {self.day} ''' ) class Teacher(People): pass class Student(People): pass a = Teacher('蔡启龙') d = Date(1994,12,10) a.birth = d a.birth.tell_birth(a) ''' ===蔡启龙的出生日期=== 年 1994 月 12 日 10 '''
练习---学生选课系统
''' 选课系统需求: 1.学生类,老师类, 学生和老师都有课程属性, 每一门课程都是一个对象. 课程: 课程名字,课程周期,课程价钱 2.学生和老师都有选择课程的功能, 还有打印所有课程的功能. ''' class People: def __init__(self,name): self.name = name self.course_lt = [] #选择一门课程的方法 def choose_course(self,obj): self.course_lt.append(obj) print(f'{self.name}选择了{obj.course_name}课程') def prt_course(self): print(f'{self.name}的课程信息为:') for i in self.course_lt: print(f''' 课程名: {i.course_name} 课程周期: {i.period} 课程价格: {i.price} ''') class Course: def __init__(self,course_name,period,price): self.course_name = course_name self.period = period self.price = price class Student(People): def __init__(self,name,score): super().__init__(name) class Teacher(People): def __init__(self,name,level): super().__init__(name) teacher_cql = Teacher('蔡启龙',10) stu_tank = Student('tank',0) python = Course('python',6,'2万') linux = Course('linux',4,1) teacher_cql.python = python teacher_cql.linux = linux teacher_cql.choose_course(teacher_cql.python) teacher_cql.choose_course(teacher_cql.linux) teacher_cql.prt_course() ''' 蔡启龙选择了python课程 蔡启龙选择了linux课程 蔡启龙的课程信息为: 课程名: python 课程周期: 6 课程价格: 2万 课程名: linux 课程周期: 4 课程价格: 1 '''
封装
什么是封装?
- 比喻
- 封:比如把一个袋子封起来
- 装:比如把一堆小猫,小狗,nick装到袋子里
- 对象就好比一个袋子,袋子里面装一堆属性
- 封装指的是把一堆属性(特征与技能)封装到一个对象---存
- 对象可以通过
.
的方式获取属性---取
为什么要封装?
- 方便存取,可以通过
对象.
的方式获取属性
如何封装?
- 在类内部,定义一堆属性(特征与技能)
- 特征:变量---数据属性
- 技能---方法属性
- 通过
对象.属性 = 属性值
修改属性
访问限制机制
什么是访问限制机制?
- 在类内部定义,凡是以
__
开头的数据属性与方法属性,都会被python隐藏起来,外部不能直接访问这些属性,比如__name = 'tank'
为什么要有访问限制机制?
- 对重要重要及隐私数据获取的逻辑更加严谨,保证数据的安全性
怎么实现
-
隐私属性通过封装一个接口,在接口内做业务逻辑处理,再把数据返回给调用者.
class Foo: # 数据属性 __name = 'tank' # 方法属性 def __run(self): print('running...') # 获取数据接口 def get_name(self): return self.__name # 修改属性接口 def set_name(self): self.__name = 'json_sb' return self.__name foo = Foo() # print(foo.__name) #AttributeError: 'Foo' object has no attribute '__name' res = foo.get_name() # 获取属性 print(res) # print(foo._Foo__name) #强制访问 foo.set_name() # 修改属性 print(foo.get_name()) # 获取修改后的属性
-
在python中不会强制限制属性的访问,类内部使用
__属性名
定义的属性变形成了_类名__属性名
,若想直接访问,可以通过对象.变形后的名字
访问class Foo: __name = 'tank' # --> _Foo__name
练习
-
class Teacher: # 初始化对象定制属性 def __init__(self, name, age, gender): self.__name = name self.__age = age self.__gender = gender # 打印用户信息接口 def prt_userinfo(self): username = input('请输入用户名:') userpwd = input('请输入用户密码:') if username == '蔡启龙' and userpwd == '123': print(f''' 用户信息为: 姓名: {self.__name} 年龄: {self.__age} 性别: {self.__gender} ''') # 修改用户信息接口 def change_userinfo(self, name, age, gender): if not isinstance(name, str) or not isinstance(age, int) or not isinstance(gender, str): raise TypeError('非法输入!') # 抛错 else: self.__name = name self.__age = age self.gender = gender teacher = Teacher('蔡启龙', 18, 'male') # 实例出一个老师对象 # teacher.prt_userinfo() #通过打印用户信息接口打印用户信息 teacher.change_userinfo('tank', 19, 'female') # 通过修改用户信息接口修改用户信息 teacher.prt_userinfo() # 通过打印用户信息接口打印修改后的用户信息
class ATM: def __insert_card(self): print('插卡...') def __input_pwd(self): print('输入密码...') def __input_amount(self): print('输入金额...') def __get_money(self): print('正在吐钱...') def __prt_flow(self): print('打印账单...') # 取款接口 def withdraw(self): self.__insert_card() self.__input_pwd() self.__input_amount() self.__get_money() self.__prt_flow() atm = ATM() # 实例化出ATM对象 atm.withdraw() # 通过取款接口取款,保证取款流程不被修改
property装饰器
什么是property?
- python内置的装饰器,主要是给类内部的方法使用
为什么要用property?
- 在对象调用某个方法时, 将调用方式从
对象.方法()
变成对象.方法
,使方法属性看起来像数据属性
怎么实现?
''' 计算人体的bmi值: bmi值=体重/(身高**2) ''' class People: def __init__(self,weight,hight): self.weight = weight self.hight = hight #计算bmi值的函数 @property def bmi(self): return self.weight/self.hight**2 p = People(60,1.75) print(p.bmi) # p.bmi = 10 #AttributeError: can't set attribute
- 使用
@property
函数装饰 - 不能对被装饰的方法进行属性修改
多态
什么是多态
- 面向对象的三大基本特征之一,依赖于继承
- 一个父类可以有不同的子类
- 父类中的一个方法,在不同子类中可以有不同执行方式,产生不同的执行结果
为什么要有多态
对于不同子类之间存在的差异(多态性),可以在父类中写出通用的抽象方法,做出通用的编程,来适应需求的不断变化
如何实现?
-
子类重用父类的方法并重写该方法来覆盖父类原有的方法
class Animal: def speak(self): print('动物说话...') class Dog(Animal): def speak(self): print('狗汪汪汪的说') class Pig(Animal): def speak(self): print('猪哼哼哼地说') class Cat(Animal): def speak(self): print('猫喵喵喵地说') dog = Dog() pig = Pig() cat = Cat() dog.speak() pig.speak() cat.speak()
-
通过抽象类实现---需导入 abc---(abstract class) 模块
-
抽象类
-
是什么
- 一系列类中相同特征与技能的结合体
- 只能被继承,不能实例化,子类必须实现抽象类中定义的每个抽象方法
-
为什么
- 多态的一个表现形式,通过关注抽象类方法的描述而不需要考虑实现细节,有利于协同开发,实现归一化设计
-
如何实现
-
导入 abc---(abstract class) 模块
import abc class Animal(metaclass=abc.ABCMeta): @abc.abstractmethod def speak(self): print('动物说话...') @abc.abstractmethod def eat(self): pass class Dog(Animal): def speak(self): print('狗汪汪汪的说') def eat(self): print('狗吃骨头...') #派生新方法 def run(self): print('狗奔跑...') dog = Dog() dog.speak() dog.eat() dog.run() ''' 没有实现抽象方法时报错: Can't instantiate abstract class Dog with abstract methods eat '''
-
-
-
鸭子类型---Duck typing
美国印第安纳州的诗人的诗句:" When I see a bird that walks like a duck and swims like a duck and quacks like a duck, I call that bird a duck."
什么是鸭子类型
任何拥有像鸭子一样走和叫方法的对象所属的类型都是鸭子类型,鸭子类型不管制对象的类型,而是关注对象具有的方法
为什么要有鸭子类型
多态的一个表现形式,鸭子类型耦合度低,使用鸭子类型可使程序扩展性增强
如何实现
既不使用抽象类也不使用继承
class Animal: def speak(self): print('动物说话...') def eat(self): pass class Dog: def speak(self): print('狗汪汪汪的说') def eat(self): print('狗吃骨头...')
定义统一接口利用多态性对接口进行多种实现
# 例一 class Animal: def speak(self): print('动物说话...') class Dog(Animal): def speak(self): print('狗汪汪汪的说') class Pig(Animal): def speak(self): print('猪哼哼哼地说') class Cat(Animal): def speak(self): print('猫喵喵喵地说') #定义统一接口 def Bark(obj_animal): obj_animal.speak() dog = Dog() pig = Pig() cat = Cat() Bark(dog) Bark(pig) Bark(cat) print('*'*50) # 例二 str1 = '1234' list1 = [1,2,3] #类的内置方法 print(str1.__len__()) print(list1.__len__()) print('*'*50) #内置 "len()" 函数---统一接口 print(len(str1)) print(len(list1)) print('*'*50) #自定义 "len()" 函数---统一接口 def my_len(d): return d.__len__() print(my_len(str1)) print(my_len(list1))