封装
1.定义
1.数据角度来讲,将一些基本数据类型复合成一个自定义类型
优势:更符合人类的思考方式,将数据与对数据的操作整合在一起
2.行为角度来讲,向类外提供必要的功能,影藏实现的细节
优势:以“模块化”的方式进行变成,可以集中精力设计组织,指挥多个类协同操作
3.设计角度来来讲
(1).分为治之
--将一个大的需求分解为许多类,每个类处理一个独立的功能
--拆分好处:便于分工,便于复用,可扩展性强
(2).变则疏之
--变化的地方独立封装,避免影响其他类
(3).高内聚
--类中的各个方法都在完成一项任务(单一职责的类)
(4).低耦合
--类与类的关联性与依赖度要低
私有成员
在Python里边,一个类的成员(成员变量、成员方法)是否为私有,完全由这个成员的名字决定。如果一个元成员的名字以两个下划线__开头,但不以两个下划线__结尾,则这个元素为私有的(private);否则,则为公有的(public)。Pyhton里边并没有protected的概念。
为了方便表述,如果一个元素的名字以两个下划线__开头,但不以两个下划线__结尾,我们称这个元素的名字“符合私有定义”。
我们注意到,私有成员不以以两个下划线结尾;所有的运算符重载相关方法,以一些特殊的成员方法如构造函数,都是以两个
下划线开头,两个下划线结尾,而且它们都是公有的。私有成员,即只能在这个类里边访问;如果你在类外面访问一个私有成员,系统会抛出一个异常,提示你这个成员不存在。
请看如下代码:
class Hugo:
def __init__(self):
self.__name = "hugo"
def Say(self):
# 在类内部使用私有成员变量__name
print("my name is:", self.__name)
boy = Hugo()
boy.Say() # OK
# 此处抛出一个异常,提示__name不存在
print("name of boy:", boy.__name)
其它运行结果如下:
my name is: hugo
Traceback (most recent call last):
File "eg1.py", line 13, in <module>
print("name of boy:", boy.__name)
AttributeError: 'Hugo' object has no attribute '__name'
正如这个例如所展示的,我们可以在类的内部(成员方法中)使用私有变量__name。然而,当我们直接在外部访问__name,会收到一个异常,说Hugo类的对象没有__name方法。显然,这个异常的提示具有误导性,因为__name实际上存在,只是不能直接访问。
1、私有成员并非真正私有
其实,我们还是可以类的外面访问私有成员,方法是,在私有成员的名字前面加一个下划线和类名。
如下面例子所示:
class Hugo:
def __init__(self):
self.__name = "hugo"
def Say(self):
# 在类内部使用私有成员变量__name
print("my name is:", self.__name)
boy = Hugo()
boy.Say() # OK
# 通过_Hugo__name可以在外部访问私有变量__name
print("name of boy:", boy._Hugo__name)
我们可以通过_Hugo__name可以在外部访问私有变量__name。然后,在实际开发中,如果没有特殊的需要,请不要这么做。我们将一个成员声明为私有,是有一定的原因的,其中一个最主要的原因,就是不希望使用者直接访问它。虽然我们还是可以这么做,但请务必遵守这个约定,以免出现不必要的问题。
2、私有访问约定
,在一个模块中,如果一个函数的名字符合私有定义,那么这个方法是私有的,只能在这个模块中使用。其实,扩展到模块中定义的类和变量也一样,如果模块中的类或者变量名符合私有定义,那么它就是私有的,只能在这个模块中使用。
但实现上,即便一个元素的名字符合私有定义,依然可以在模块外使用。
不过,在理念上,如果一个函数的名字符合私有定义,那么它就是一种私有访问的约定,或者惯例,没有特殊的原因,你就不应该去直接使用它。
在大型项目开发过程中,为了提高协作的效率,我们会有很多不成文的约定(通常被称为“惯例”)。当我们见到一个东西符合某种约定,那我们最好就遵循这个惯例。
# 使用方法,封装变量
class Wife:
def __init__(self, name, age, weight):
self.name = name
# 本质:障眼法(实际将变量名改为:_类名__age)
self.__age = age
self.__weight = weight
w01 = Wife("铁扇公主", 87, 87)
# 从新创建新实例变量
w01._Wife__age = 107 # 修改了类中定义的私有变量
print(w01.name)
print(w01._Wife__age) #python 内置变量,存储对象的实例变量都在里面
输出结果
铁扇公主
107
{'name': '铁扇公主', '_Wife__age': 107, '_Wife__weight': 87}
class Wife:
def __init__(self, name, age, weight):
self.name = name
# 本质:障眼法(实际将变量名改为:_类名__age)
self.__age = age
self.__weight = weight
# 提供公开的读写方法
def get_age(self):
return self.__age
def set_age(self,value):
if 21 <= value <= 31:
self.__age = value
else:
raise ValueError("我不要")
w01 = Wife("牛魔王",30,87)
w01.set_age(25)
print(w01.get_age())
创建property对象,只负责拦截读取操作,变量…setter 只负责拦截写入操作
class Wife:
def __init__(self, name, age, weight):
self.name = name
# 本质:障眼法(实际将变量名改为:_类名__age)
self.age = age
self.weight = weight
# 提供公开的读写方法
@property # 创建property对象,只负责拦截读取操作
def age(self):
return self.__age
@age.setter # 只负责拦截写入操作
def age(self, value):
if 21 <= value <= 31:
self.__age = value
else:
raise ValueError("我不要")
@property
def weight(self):
return self.__weight
@weight.setter
def weight(self, value):
if 32<= value <= 60:
self.__weight = value
else:
raise ValueError("我不要")
w01 = Wife("牛魔王", 30, 87)
w01.age = 25
print(w01.age)
w01.weight = 45
print(w01.weight)
练习1:定义敌人类(姓名,攻击力10–50),血量100–200创建一个敌人对象,可以修改数据,读取数据
class Enemy:
def __init__(self,name,hp, atk):
self.name = name
self.__hp = hp
self.__atk = atk
def get_hp(self):
return self.__hp
def set_hp(self,value):
if 10 <= value <= 50:
self.__hp = value
else:
raise ValueError("我不要")
def get_atk(self):
return self.__atk
def set_atk(self,value):
if 100 <= value <= 200:
self.__atk = value
else:
raise ValueError("我不要")
w01 = Enemy("面霸",80,120)
w01.set_hp(25)
print(w01.get_hp())
w01.set_atk(150)
print(w01.get_atk())
输出结果
25
150
继承
多个子类在概念上一致,所以就抽象出一个父类
多个子类的共性,可以提取到父类中
从设计角度讲:先有子再有父
从编码角度讲:先有父,再有子
子类不用写,但是可以用
子类对象可以调用子类成员,也可以调用父类成员
父类对象只可以调用父类成员,不能调用子类成员
# Python 内置函数isinstance
# 1.判断对象是否属于一个类型
# ”老师对象“是 一个老师类型
print(isinstance(t01,Teacher)) # True
# "老师对象" 是 一个学生类型
print(inistance(t01,Student)) # False
# "老师对象" 是 一个人类型
print(isinstance(t01,Person)) # True
class Person:
def say(self):
print("说话")
class Student(Person):
def study(self):
print("学习")
class Teacher(Person):
def teach(self):
print("讲课")
子类若具有构造函数,则必须先调用父类构造函数
class Person:
def __init__(self,name)
self.name = name
class Student(person):
def __init__(self,name,score):
super().__init__(name)
self.score = score
s01 = Student("张三",100)
print(s01.score)
print(s01.name)
多态
1.定义
父类的同一种动作或者行为,在不同的子类上有不同的实现
2.作用
1.继承相关概念的共性进行抽象,多态在共性的基础上,提现行为有不同的实现
2.争强可扩展性,提现开闭原则
类与类的关系
泛化:子类与父类的关系,概念的复用,耦合度最高
B类泛化A类,意味着B类是A类的一种
做法:B类继承A类
关联(聚合/组合):部分与整体的关系,功能的复用,变化影响一个
A与B关联,意味着B是A的一部分
做法:在A类中包含B类成员
依赖:合作关系,是一种相对松散的协作,变化影响一个方法
A类依赖B类,意味着A类中方法的参数并不是A的成员