组合
什么是组合
组合就是一个类的对象具备某一个属性,该属性的值是指向另外一个类的对象
为什么要使用组合
为了减少类与类之间代码的冗余
如何使用组合
class Course:
def __init__(self, course_name, course_period):
self.course_name = course_name
self.course_period = course_period
def tell_info(self):
print(f'''
课程信息
课程名称:{self.course_name}
课程时长:{self.course_period}
''')
class Student:
def __init__(self, name, age, gender):
self.name = name
self.age = age
self.gender = gender
self.course_list = []
def choose_course(self, course_obj):
self.course_list.append(course_obj)
def tell_course_info(self):
for course in self.course_list:
course.tell_info()
# 创建课程和学生对象
python = Course('python', '6个月')
stu = Student('Tiny', 18, 'male')
#添加课程
stu.choose_course(python)
# 获取课程信息
stu.tell_course_info()
封装
什么是封装
封装指的是将一堆属性(特征和技能)放到一个对象中
为什么要封装
- 封装数据的原因:保护隐私
- 封装方法的原因:隔离复杂度
两个层面的封装
封装其实分为两个层面,但无论哪种封装,都要对外界提供好访问你内部隐藏内容的接口(接口可以理解为入口,有了这个入口,使用者无需且不能够直接访问到内部隐藏的细节,只能走接口,并且我们可以在接口的实现上附加更多的处理逻辑,从而严格控制使用者的访问)
第一个层面的封装
创建类和对象会分别创建二者的名称空间,我们可以用类名.或者对象.的方式去访问里面的名字,这本身就是一种封装
注意:对于这一层面的封装,类名.和对象名.就是访问隐藏属性的接口
第二个层面的封装
类中把某些属性和方法隐藏起来(或者说定义成私有),只有类的内部使用,外部无法访问,或者留下少量接口(函数)供外部使用
在python中用__(双下划线)的方式来实现隐藏属性(私有化)
类中所有双下划线开头的名称如__x都会自动变成:_类名__x的形式:
class A:
__name = 'Tiny'
def __foo(self):
print('from A')
def get_name(self):
return self.__name
def set_name(self):
self.__name = 'Nick'
a = A()
print(a.get_name())
a.set_name()
print(a.get_name())
print(a._A__name)
Tiny
Nick
Nick
这种自动变形的特点:
- 类中定义的__name只能在类中使用,如self.__name
- 这种变形其实正是针对内部的变形,在外部是无法通过__name这个名字访问到的
- 在子类定义的__name不会覆盖在父类定义的__name,因为子类中变形成了_子类名__name,而父类中变形成了_父类名__name,即双下划线开头的属性在继承给子类时,子类是无法覆盖的
注意:对于这一层面的封装,我们需要在类中定义一个函数(接口函数)在它内部访问被隐藏的属性,然后外部就可以使用了
这种变形需要注意的问题是:
- 这种机制也并没有真正意义上限制我们从外部直接访问,知道了类名和属性名就可以拼出名字,_类名__属性名,然后就可以访问了
- 变形的过程只在类的定义时发生一次,在定义后的赋值操作,不会变形
- 在继承中,父类如果不想让子类覆盖自己的方法,可以将方法定义为私有的
类的property特性
什么是property特性
property装饰器用于将被装饰的方法伪装成一个数据属性,在使用时可以不加括号而直接使用
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 * self.height)
@property
def get_name(self):
return self.name
@get_name.setter
def set_name(self, val):
self.name = val
people = People('tiny', 54, 1.7)
print(people.bmi)
people.set_name = 'jack'
print(people.get_name)
property属性的定义和调用要注意以下几点:
- 定义是,在实例方法的基础上添加@property装饰器;并且仅有一个self参数
- 调用时,无需括号
类的多态和多态性
什么是多态
多态指的是一类事物有多种形态
- 序列数据类型有多种形态:字符串,列表,元组
- 动物有多种形态:人,狗,猪
什么是多态性
多态性是指具有不同功能的函数可以使用相同的函数名,这样就可以用一个函数名调用不同内容的函数.
class Pig:
def run(self):
print('pig is running')
class Dog:
def run(self):
print('dog is running')
pig = Pig()
dog = Dog()
pig.run()
dog.run()
抽象类
什么是抽象类
abc模块 abstract_class
为什么使用抽象类
强制子类必须遵循父类的一套标准
如何使用抽象类
import abc
class Animal(metaclass=abc.ABCMeta):
@abc.abstractmethod
def run(self):
pass
class Pig(Animal):
def run(self):
print('pig is running')
class Dog(Animal):
def run(self):
print('dog is running')
pig = Pig()
dog = Dog()
pig.run()
dog.run()
鸭子类型
什么是鸭子类型
Duck typing 这个概念来源于美国印第安纳州的诗人詹姆斯·惠特科姆·莱利(James Whitcomb Riley,1849-
1916)的诗句:”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.”
在python中,不推荐使用抽象类来强制限制子类的定义,但是推荐都遵循鸭子类型
继承:
耦合性太高,程序的可扩展性差
鸭子类型:
耦合性低,程序的可扩展性强