Python进阶之封装

【一】面向对象的三大特性

  • 面向对象的三大特性:封装、继承、多态
  • 其中最重要的就是封装,封装就是将数据和功能整合到一起
  • 我们可以对封装在类和对象中的属性进行访问的控制,有隐藏的和开发的接口

【1】什么是封装

  • 封装是对具体对象的一种抽象
  • 封装就是将某些数据和功能隐藏起来,只能通过程序内部查看,而外部看不了

【2】为什么要封装

  • 封装就是为了保护隐私,不让别人知道内部的功能

【3】封装的方法

  • 你的身体的每一个器官,每一块皮肤,以至于你这整个人,都是属于自己一个人的,也只有你一个人可以使用,别人也用不了,也看不到你怎么用的

【二】隐藏属性

【1】隐藏属性方法

  • 采用双下划线开头的方式将属性隐藏起来
  • 类中所有双下滑线开头的属性都会在类定义阶段、检测语法时自动变成_类名__属性名的形式
class Student:
    __SCHOOL = "北京大学"

    # 定义函数时,会检测函数语法,所以__开头的属性也会变形
    def __init__(self, name):
        self.__name = name
        
    # 定义函数时,会检测函数语法,所以__开头的属性也会变形
    def __read(self):
        print(f"学生{self.__name}的学校是{self.__SCHOOL}")


student = Student('ligo')
print(student.name)
# AttributeError: 'Student' object has no attribute 'name'

【2】隐藏属性访问

  • 可以通过__类名__变量名来从类外部访问属性
class Student:
    __SCHOOL = "北京大学"

    # 定义函数时,会检测函数语法,所以__开头的属性也会变形
    def __init__(self, name):
        self.__name = name

    # 定义函数时,会检测函数语法,所以__开头的属性也会变形
    def __read(self):
        print(f"学生{self.__name}的学校是{self.__SCHOOL}")


# 查看当前类的名称空间
print(Student.__dict__)
# '_Student__SCHOOL': '北京大学'
# 发现可以通过特定的方法,仍然可以访问到相应的属性 _Student__SCHOOL
print(Student.__SCHOOL) #找不到
print(Student._Student__SCHOOL)  # 北京大学(可以找到)

student = Student('ligo')
# 查看对象的名称空间
print(student.__dict__)
# {'_Student__name': 'ligo'}
# 也可以访问到类里的函数_Student__read
student._Student__read()
# 学生ligo的学校是北京大学

【3】隐藏方法变形

  • 在类内部是可以直接访问双下滑线开头的属性的
class Student:
    __SCHOOL = "北京大学"

    # 定义函数时,会检测函数语法,所以__开头的属性也会变形
    def __init__(self, name):
        self.__name = name

    # 定义函数时,会检测函数语法,所以__开头的属性也会变形
    def __read(self):
        print(f"学生{self.__name}的学校是{self.__SCHOOL}")
        
    # 直接在内部调用
    def read(self):
        self.__read()
        print(self.__name)


student = Student('ligo')
student.read()
# 学生ligo的学校是北京大学
# ligo

【4】变形操作只会发生一次

  • 变形操作只在类定义阶段发生一次,在类定义之后的赋值操作,不会变形
class Student:
   __SCHOOL = "北京大学"

   # 定义函数时,会检测函数语法,所以__开头的属性也会变形
   def __init__(self, name):
       self.__name = name

   # 定义函数时,会检测函数语法,所以__开头的属性也会变形
   def __read(self):
       print(f"学生{self.__name}的学校是{self.__SCHOOL}")


# 修改类的变量
Student.__SCHOOL = '清华大学'
# 查看当前类的名称空间
print(Student.__dict__)
# '_Student__SCHOOL': '北京大学'
# 将__SCHOOL改为_Student__SCHOOL
Student._Student__SCHOOL = '清华大学'
print(Student.__dict__)

student = Student('ligo')
# 修改对象变量
student.__name = 'scott'
# 查看对象的名称空间
print(student.__dict__)
# {'_Student__name': 'ligo', '__name': 'scott'}
# 可以直接根据键取值
print(student.__name)  # scott
student._Student__read()
# 学生ligo的学校是清华大学

【三】开放接口

  • 定义属性就是为了使用,所以隐藏并不是目的

【1】隐藏数据属性

  • 将数据隐藏起来就限制了类外部对数据的直接操作,然后类内应该提供相应的接口来允许类外部间接地操作数据,接口之上可以附加额外的逻辑来对数据的操作进行严格地控制
class Student(object):
    def __init__(self, name, school):
        self.__name = name
        self.__school = school

    # 对外提供访问学生信息的接口
    def read(self):
        print(f"学生{self.__name}的学校是{self.__school}")

    # 对外提供设置学生信息的接口,并附加类型检查的逻辑
    def set_info(self, name, school):
        if not isinstance(name, str):
            raise TypeError('学生姓名必须是字符串类型的')
        if len(school) != 4:
            raise TypeError('必须是四字大学')
        self.__name = name
        self.__school = school


# 实例化类得到对象
student = Student(name='ligo', school='北京大学')
student.read()  # 学生ligo的学校是北京大学
# 修改
student.set_info(name='ligo', school='清华大学')
student.read()  # 学生ligo的学校是清华大学

# 不符合要求会抛出异常
student.set_info(name='ligo', school='大学')
student.read()  # TypeError: 必须是四字大学

【2】隐藏函数属性

  • 目的的是为了隔离复杂度
  • 例如ATM有插卡、身份认证、输入金额、打印小票、取钱等功能,对于使用者只需要开放取款功能的接口就行了,其余功能我们都可以隐藏起来
class ATM:
    def __card(self):
        print('插卡')

    def __auth(self):
        print('用户认证')

    def __input(self):
        print('输入取款金额')

    def __print_bill(self):
        print('打印账单')

    def __take_money(self):
        print('取款')

    # 取款功能
    def withdraw(self):
        self.__card()
        self.__auth()
        self.__input()
        self.__print_bill()
        self.__take_money()


obj = ATM()
obj.withdraw()
# 插卡
# 用户认证
# 输入取款金额
# 打印账单
# 取款

【3】小结

  • 隐藏属性与开放接口,是为了明确地区分内外,类内部可以修改封装内的东西而不影响外部调用者的代码
  • 而外部使用者只需拿到一个接口,只要接口名、参数不变,使用者的代码永远无需改变
  • 只要接口这个基础不变,代码的无论怎么修改都没有影响

【四】property

【1】什么是property

  • property是一种特殊的属性,执行函数得到返回值,然后作为数据属性返回
class Student(object):
    school = "清华大学"

    def __init__(self, name):
        self.name = name

    @property
    def read(self):
        return f"学生{self.name}的学校是{self.school}"


student = Student(name='ligo')
print(student.name)  # ligo
print(student.read)  # 学生ligo的学校是清华大学

【2】BMI例子

  • 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 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 ** 2)


people = People('ligo', 80, 1.80)

# 触发方法bmi的执行,将people自动传给self,执行后返回值作为本次引用的结果
# 正常我们调用对象的方法应该是people.bmi(),但是这里我们没有加()就能正常调用
print(people.bmi)  # 24.691358024691358

【3】为什么要用property

  • 面向对象的封装有三种方式:
    • public:不封装,是对外公开的
    • protected:对外不公开,但对朋友或者子类公开,就是对内公开
    • private:对谁都不公开
  • public
class Student(object):
    def __init__(self, name):
        self.name = name


student = Student(name='ligo')
# 查看姓名
print(student.name)  # ligo
# 修改姓名
student.name = 'scott'
print(student.name)  # scott
# 删除姓名
del student.name
print(student.name)
# AttributeError: 'Student' object has no attribute 'name'
  • protected
class Student(object):
    def __init__(self, name):
        self.__name = name
        
    @property
    def name(self):
        return self.__name

    @name.setter
    def name(self,value):
        # 通过类型检查后,将值value存放到真实的位置self.__name
        self.__name = value

    @name.deleter
    def name(self):
        del self.__name


student = Student(name='ligo')
# 查看姓名
print(student.name)  # ligo
# 修改姓名
student.name = 'scott'
print(student.name)  # scott
# 删除姓名
del student.name
print(student.name)
# AttributeError: 'Student' object has no attribute 'name'
  • private
class Student(object):
    def __init__(self, name):
        self.__name = name

    def name(self):
        return self.__name

    def show_name(self, value):
        # 通过类型检查后,将值value存放到真实的位置self.__name
        self.__name = value

    def del_name(self):
        del self.__name
        
    # 不使用装饰器,而是使用 包装的 形式
    name = property(name, show_name, del_name)


student = Student(name='ligo')
# 查看姓名
print(student.name)  # ligo
# 修改姓名
student.name = 'scott'
print(student.name)  # scott
# 删除姓名
del student.name
print(student.name)
# AttributeError: 'Student' object has no attribute 'name'
posted @   Ligo6  阅读(24)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
点击右上角即可分享
微信分享提示