面向对象
【一】面向对象的三大特性
-
继承
-
封装
-
封装:就是将数据与功能整和到一起
-
类里面定义数据和方法的时候,都会有一个需求,某些方法和属性是不能被别人看到的
-
-
多态
【二】什么是封装
-
封装就是对具体对象的一种抽象
-
就是将某部分的功能和代码隐藏起来,在程序内部看不到,只能在程序内部使用
-
封装最主要的原因就是保护隐私,将不想让用户看到的功能都隐藏起来
-
**封装的意思就像是隐私,你想让他会看到的就让他看到,不想让它看到的就隐藏起来**
【三】封装隐藏属性
【1】在变量名前面加__
-
例子:先定义一个类,然后在里面写函数,不想让用户看到的就在变量名前加__
class Student():
#在变量名前加__:初始化对象的时候就会将变量名进行变形,变形成
#_Student__SCHOOL_NAME:就是变成,_类名__变量名
__SCHOOL_NAME="清华"
def __init__(self,name):
self.name = name
student = Student(name="dream")
#得到名字
print(student.__dict__)#{'name': 'dream'}
#对参数值进行修改
student.name = "opp"
print(student.name)#opp
#查看名称空间
print(student.__dict__)#{'name': 'opp'}
# print(Student.__SCHOOL_NAME)#找不到报错:类型对象'Student'没有属性'_SCHOOL_NAME'
print(Student._Student__SCHOOL_NAME)#清华
print(Student.__dict__)#在里面能找到这个:'_Student__SCHOOL_NAME': '清华'
【2】函数属性
-
__dict__
:查询函数属性,得到的是个字典 -
__init__
:魔法方法--》也称为初始化方法,用于初始化对象状态,用的时候随时调用
class Person:
SCHOOL_NAME = "清华"
def __init__(self, name):
self.name = name
def __change_name(self):
self.name = "nb_" + self.name
print("进来了")
student = Person(name="tom")
print(student)#<__main__.Person object at 0x0000026C87D5FFD0>
print(student.__dict__)#{'name': 'tom'}
print(student.__init__)#<bound method Person.__init__ of <__main__.Person object at 0x0000026C87D5FFD0>>
print(Person.__dict__)#里面有很多属性就列举了一个:{ 'SCHOOL_NAME': '清华'}
print(Person)#<class '__main__.Person'>
print(person._Person__change_name())#不带前缀_Person会报错,表示找不到
#进来了
#没有返回值所以同时返回了:None
【3】什么是初始化过程
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
# 创建一个新的Person对象
person1 = Person("Alice", 30)
# 此时,person1 对象已经有了 name 和 age 属性
print(person1.name) # 输出: Alice
print(person1.age) # 输出: 30
在这个例子中,当您调用 Person("Alice", 30) 时,Python 创建了一个新的 Person 对象,并调用了 Person 类的 __init__ 方法来设置该对象的 name 和 age 属性。这就是我们通常所说的“变形”或“初始化”过程。
【4】补充
-
变形只会发生一次
-
变形只会发生在初始化的时候
class Person:
__SCHOOL_NAME = "清华"
student = Person()
# print(student.__SCHOOL_NAME)#会报错
student.__SCHOOL_NAME = '北大'
print(student.__SCHOOL_NAME)#北大
print(Person.__dict__)#{'_Person__SCHOOL_NAME': '清华'}
print(student.__dict__)#{'__SCHOOL_NAME': '北大'}
#这两个不是一个东西:print(Person.__dict__)和print(student.__dict__)
#student.__SCHOOL_NAME = '北大':只是加到了student属性里面
【四】什么是开放接口
-
定义属性,隐藏属是为了不让用户看到对应的功能和逻辑
-
但是我们要给用户对应的修改接口
class Teacher:
def __init__(self, name, age):
# 将名字和年纪都隐藏起来
self.__name = name
self.__age = age
# 对外提供访问老师信息的接口
def tell_info(self):
print('姓名:%s,年龄:%s' % (self.__name, self.__age))
# 对外提供设置老师信息的接口,并附加类型检查的逻辑
def set_info(self, name, age):
if not isinstance(name, str):
raise TypeError('姓名必须是字符串类型')
if not isinstance(age, int):
raise TypeError('年龄必须是整型')
self.__name = name
self.__age = age
# 实例化类得到对象
teacher = Teacher("dream", 18)
# 代用对象中的方法,修改对象的信息
# age 必须是数字类型,但是给了字符串,所以一定会报错
teacher.set_info('hope', '22')
#报错信息:r'''
Traceback (most recent call last):
File "E:\PythonProjects\10面向对象\02封装.py", line 140, in <module>
teacher.set_info('hope', '22')
File "E:\PythonProjects\10面向对象\02封装.py", line 131, in set_info
raise TypeError('年龄必须是整型')
TypeError: 年龄必须是整型
'''
# 调用对象的方法修改对象的信息 , 符合要求就能修改成功
teacher.set_info('Hope', 22)
# 查看对象的信息
teacher.tell_info()
# 姓名:Hope,年龄:22
-
总结
-
总结隐藏属性与开放接口,本质就是为了明确地区分内外,类内部可以修改封装内的东西而不影响外部调用者的代码;
-
而类外部只需拿到一个接口,只要接口名、参数不变,则无论设计者如何改变内部实现代码,使用者均无需改变代码。
-
这就提供一个良好的合作基础,只要接口这个基础约定不变,则代码的修改不足为虑。
-
【五】装饰器property拓展
-
property是一种特殊的属性,将函数的返回值作为数据属性返回
class Student:
def __init__(self, name):
self.name = name
@property
def vip_name(self):
return self.name
student = Student(name='dream')
print(student.name)
# 不加property之前
# print(student.vip_name) # <bound method Student.vip_name of <__main__.Student object at 0x000001F2944AB190>>
# print(student.vip_name()) # dream
# 加了property之后
print(student.vip_name) # dream
print(student.vip_name()) # 加了property之后,后面就不能有(),有就会报错
【1】BIM例子
# 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='dream', weight=64, height=1.7)
print(dream.bmi)
【2】为什么使用property
-
在编程语言中有三种封装方式
-
public:不封装,是对外公开的
-
protected:对外不公开,对内公开
-
private:对谁用不公开
-
(1)方法一
-
在使用property之后
-
修改 :@函数名.setter,要在修改的函数头上,就像用装饰器那样
-
删除:@函数名.deleter,要在修改的函数头上,就像用装饰器那样
-
#例题
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='dream')
# 查看
print(person.vip_name)
# <bound method Person.name of <__main__.Person object at 0x000001EE86C22340>>
# 修改
person.vip_name = 'opp'
print(person.vip_name)
# 删除
# del person.vip_name
# print(person.vip_name)
(2)方法二
-
使用包装的形式
-
区别就在于,它在最后,包所有函数名进行了包装
-
vip_name = property(get_vip_name, set_vip_name, del_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='dream')
# 查看
print(person.vip_name)
# <bound method Person.name of <__main__.Person object at 0x000001EE86C22340>>
# 修改
person.vip_name = 'opp'
print(person.vip_name)
# 删除
del person.vip_name
print(person.vip_name)