Ⅰ 面向对象三大特征之封装
【一】面向对象的三大特性
# 继承
# 封装
# 多态
# 其中之一就是封装:就是将数据与功能整合到一起
# 类里面定义数据和方法的时候,都会有一个需求,某些方法和属性是不能让别人看到的
【二】什么是封装
# 封装就是对具体的对象的一种抽象
# 意思就是将某部分功能和代码隐藏起来,在程序外边看不到,只能在程序内部使用
【三】为什么要封装?
# 封装最主要的原因就是为了保护隐私,将不想让用户看到的功能隐藏起来
class Student():
...
# 初始化
def __init__(self, name):
self.name = name
def change_name(self):
self.name = 'nb_' + self.name
stu1 = Student(name='silence')
stu1.change_name()
print(stu1.name) # nb_silence
【四】封装的方法
比如
# 你自己的身体其实也有封装的部分
# 膀胱,你自己有并且自己能用但是别人看不到也也用不了
# 在变量名前面加 __
【五】封装隐藏属性
【1】数据属性
class Person:
# 在变量名前面加 __
SCHOOL_NAME = '清华'
def __init__(self, name):
self.name = name
student = Person(name='silence')
print(student.name)
# 查看类的民称空间
# {'SCHOOL_NAME': '清华'}
print(Person.__dict__)
print(student.SCHOOL_NAME)
class Person:
# 并没有真正的隐藏,只是自动转换成了特定的语法
# 在变量名前面加 __ : 在类初始化对象的时候会对当前变量名进行变形 变形成 _Person__变量名
# 封装功能只在类定义阶段才生效
__SCHOOL_NAME = '清华'
def __init__(self, name):
self.name = name
student = Person(name='silence')
print(student.name)
# 查看类的民称空间
# {'_Person__SCHOOL_NAME': '清华',}
print(Person.__dict__)
print(student.__SCHOOL_NAME) # 找不到
print(student._Person__SCHOOL_NAME) # 能找到 __SCHOOL_NAME变成_Person__SCHOOL_NAME
【2】函数属性
class Person:
# 在变量名前面加 __ : 在类初始化对象的时候会对当前变量名进行变形 变形成 _Person__变量名
__SCHOOL_NAME = '清华'
def __init__(self, name):
self.name = name
# 函数属性 : 在类初始化对象的时候会对当前变量名进行变形 变形成 _Person__变量名
def __change_name(self):
self.name = 'nb_' + self.name
student = Person(name='silence')
print(student.name)
# 查看类的民称空间
# {'_Person__SCHOOL_NAME': '清华','_Person__change_name': <function Person.__change_name at 0x000001D09384C0D0>,}
print(Person.__dict__)
print(student.__change_name())
# 当我们在对象中调用相关属性的时候会发现,找不到
【3】补充
# 变形只发生在类初始化得到对象的时候
# 并且变形只会发生一次
print(student.name)
print(Person.__dict__)
student.__SCHOOL_NAME = '北大'
print(student._Person__SCHOOL_NAME)
print(Person.__dict__)
print(student.__dict__)
print(student.__SCHOOL_NAME)
【六】什么是开放接口
# 定义属性,并且隐藏属性或者方法是为了不让用户看到对应的功能和逻辑
# 但是我要给用户提供修改的接口
class Teacher:
def __init__(self, name, age):
self.__name = name
self.__age = age
def set_info(self, name, age):
# 修改名字和修改年龄之前要校验当前的格式是否正确
# 名字前面必须 + lj_
if not name.startswith('lj_'):
raise ValueError("名字必须是 lj 前缀")
# 年龄必须是数字且大于 0
if not age.isdigit():
raise ValueError("年龄必须是数字")
if int(age) < 0:
raise ValueError("年龄超出常人,建议回炉重造!")
self.__name = name
self.__age = age
# 做一个接口查看当前讲师的个人信息
def tell_info(self):
print(f"当前讲师是 :>>>> {self.__name} 年龄是 :>>>> {self.__age}")
teacher = Teacher(name="silence", age=18)
# teacher.tell_info()
#修改当前讲师的姓名和年龄
teacher.set_info(name='lj_happy',age='15')
teacher.tell_info()
【七】小结
# 隐藏属性和开放接口本质上都是为了明确的区分内外,在类内部可以随意修改我的代码,但是在类外部不允许使用者修改代码
# 类外部只需要拿到一个接口,只要接口名不变,里面的逻辑就可以随意实现
# 接口只要基础不变,代码就可以任意修改
【八】拓展:property
【1】什么是property
# 是一种特殊的属性,将函数的返回值作为数据属性返回
class Student:
def __init__(self, name):
self.name = name
@property
def vip_name(self):
return self.name
student = Student(name='silence')
print(student.name)
print(student.vip_name)
# print(student.vip_name())
【2】BMI例子
# BMI : 衡量一个人的体重和身高对健康影响的指标
# 指标
# ○ 过轻:低于18.5
# ○ 正常:18.5-23.9
# ○ 过重:24-27
# ○ 肥胖:28-32
# ○ 非常肥胖, 高于32
# 计算公式
# ○ 体质指数(BMI)=体重(kg)÷身高^2(m)
# ○ EX:70kg÷(1.75×1.75)=22.86
class BMI:
def __init__(self, name, weight, height):
self.name = name
self.weight = weight
self.height = height
@property
def bmi(self):
return self.weight / (self.height ** 2)
dream = BMI(name='silence', weight=70, height=1.7)
print(dream.bmi)
【3】为什么要使用property
# 【1】在所有编程语言中都有三种封装方式
# public : 公开
# protected : 对外不公开但是对美公开
# private : 对谁都不公开
# 【2】上面三种封装方式就用到了我们的类中
class PersonOne(object):
def __init__(self, name):
self.name = name
person_one = PersonOne(name='silence')
# 查看
print(person_one.name) # silence
# 修改
person_one.name = '666'
print(person_one.name) # 666
# 删除
del person_one.name
print(person_one.name) # 'PersonOne' object has no attribute 'name'
class Person(object):
def __init__(self, name):
self.__name = name
# 给当前函数名添加装饰器 property
# 将当前 函数名作为一个数据属性返回
@property
def vip_name(self):
# 返回值可以是字符串也可以是其他内容
return self.__name
# 修改 和 property 包装的函数名一致 并且加 .setter
# 修改当前变量民的时候会触发
@vip_name.setter
def vip_name(self, value):
print(value)
self.__name = value
# 删除 和 property 包装的函数名一致 并且加 .deleter
# 删除当前变量民的时候会触发
@vip_name.deleter
def vip_name(self):
del self.__name
person = Person(name='silence')
# 查看
print(person.vip_name)
# <bound method Person.name of <__main__.Person object at 0x000001EE86C22340>>
# 修改
person.vip_name = 'happy'
print(person.vip_name)
# 删除
del person.vip_name
print(person.vip_name)
class Person(object):
def __init__(self, name):
self.__name = name
# 给当前函数名添加装饰器 property
# 将当前 函数名作为一个数据属性返回
def get_vip_name(self):
# 返回值可以是字符串也可以是其他内容
return self.__name
# 修改 和 property 包装的函数名一致 并且加 .setter
# 修改当前变量民的时候会触发
def set_vip_name(self, value):
print(value)
self.__name = value
# 删除 和 property 包装的函数名一致 并且加 .deleter
# 删除当前变量民的时候会触发
def del_vip_name(self):
del self.__name
vip_name = property(get_vip_name, set_vip_name, del_vip_name)
person = Person(name='silence')
# 查看
print(person.vip_name)
# <bound method Person.name of <__main__.Person object at 0x000001EE86C22340>>
# 修改
person.vip_name = 'happy'
print(person.vip_name)
# 删除
del person.vip_name
print(person.vip_name)