DC's blog DC's blog
首页
  • 计算机基础
  • linux基础
  • mysql
  • git
  • 数据结构与算法
  • axure
  • english
  • docker
  • opp
  • oop
  • 网络并发编程
  • 不基础的py基础
  • 设计模式
  • html
  • css
  • javascript
  • jquery
  • UI
  • 第一次学vue
  • 第二次学vue
  • Django
  • drf
  • drf_re
  • 温故知新
  • flask
  • 前后端不分离

    • BBS
    • 订单系统
    • CRM
  • 前后端部分分离

    • pear-admin-flask
    • pear-admin-django
  • 前后端分离

    • 供应链系统
  • 理论基础
  • py数据分析包
  • 机器学习
  • 深度学习
  • 华中科大的网课
  • cursor
  • deepseek
  • 杂文
  • 罗老师语录
  • 关于我

    • me
  • 分类
  • 归档
GitHub (opens new window)

DC

愿我一生欢喜,不为世俗所及.
首页
  • 计算机基础
  • linux基础
  • mysql
  • git
  • 数据结构与算法
  • axure
  • english
  • docker
  • opp
  • oop
  • 网络并发编程
  • 不基础的py基础
  • 设计模式
  • html
  • css
  • javascript
  • jquery
  • UI
  • 第一次学vue
  • 第二次学vue
  • Django
  • drf
  • drf_re
  • 温故知新
  • flask
  • 前后端不分离

    • BBS
    • 订单系统
    • CRM
  • 前后端部分分离

    • pear-admin-flask
    • pear-admin-django
  • 前后端分离

    • 供应链系统
  • 理论基础
  • py数据分析包
  • 机器学习
  • 深度学习
  • 华中科大的网课
  • cursor
  • deepseek
  • 杂文
  • 罗老师语录
  • 关于我

    • me
  • 分类
  • 归档
GitHub (opens new window)
  • python面向过程

  • python面向对象

    • OOP基本
    • 继承和组合
    • 多态与多态性
    • 封装与接口
      • 封装
        • ☆什么是封装?
        • ☆如何封装?
        • ☆封装底层原理
        • ☆开发接口
        • 隐藏数据属性
        • 隐藏函数属性
      • @property
    • 绑定与非绑定方法
    • 简单总结
    • 反射、内置方法
    • 元类
    • 异常处理
    • 小项目之course_select
    • 复习
  • 网络并发编程

  • 不基础的py基础

  • 设计模式

  • python_Need
  • python面向对象
DC
2023-07-26
目录

封装与接口

# 封装

# ☆什么是封装?

[封:] 属性对外是隐藏的, 对内是开放的..
装: 申请一个名称空间,往里面装入一系列的名字/属性..

不用做任何操作, 类与实例化对象就满足装的概念啦;但不满足封, 因为它们的属性, 内外都能访问的到.

class People:
    country = 'China'

    def __init__(self, name, age, sex):
        self.name = name
        self.age = age
        self.sex = sex
        print(People.country)  # -- 内部访问 类属性

    def eat(self):
        print('%s is eating....' % self.name)  # -- 内部访问 实例化对象属性

    # print(People.__country)  # 注意哦!报错,因为People类在这里还没有定义完..

peo1 = People('小川', 20, 'male')
print(peo1.name)  # -- 外部访问 实例化属性
print(People.country)  # -- 外部访问 类属性
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# ☆如何封装?

在属性前加上__开头..
这种隐藏是对外不对内的, 即在类的内部可以直接访问,而在类的外部无法直接访问..

class People:
    __country = 'China'

    def __init__(self, name, age, sex):
        self.__name = name
        self.age = age
        self.sex = sex

    def speak(self):
        print('is speaking...')
        print(People.__country)

    def eat(self):
        print('is eating...')
        print(self.__name)

    def __run(self):
        print('is running...')


# -- 类属性 内部能访问,外部不能
People.speak(123)
# People.__run(123)  # type object 'People' has no attribute '__run'
# print(People.__country)  # type object 'People' has no attribute '__country'

# -- 实例化对象属性 内部能访问,外部不能
peo1 = People('小川', 20, 'male')
peo1.eat()
# print(peo1.__name)  # 'People' object has no attribute 'name'
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

# ☆封装底层原理

封装的隐藏属性的底层原理/特定:
1> 这种隐藏仅仅只是一种 语法上的变形操作 _类名__属性名
2> 这种语法上的变形 只在类定义阶段发生一次 .. 因为类体代码仅仅只在类定义阶段检测一次..
3> 这种隐藏是 对外不对内 的,即在类的内部可以直接访问,而在类的外部无法直接访问, 原因是
     在类定义阶段, 类体内代码 统一 发生了一次变形...
4> 如果不想让子类的方法覆盖父类的, 可以将该方法名前加一个__开头.

当运行到类体代码,会从上到下,先检测当前行的语法(遇到函数的话,也会先检测函数体里的代码), 在检测过程中就将封装属性的名字进行了变形!检测后执行当前行代码.丢进名称空间的名字就变成了检测时变形的名字. 所以能在内部可以直接访问到,在外部不能直接访问到..
阐述的好啰嗦... 反复横跳 只可意会不可言传.(´▽`)

# {'_People__name': '小川', 'age': 20, 'sex': 'male'}
print(peo1.__dict__)
# {... '_People__country': 'China', ... ,
# '_People__run': <function People.__run at 0x7fedb7f1cc10> ...}
print(People.__dict__)
1
2
3
4
5

这意味着,在类体外,以封装的方式试图添加一个隐藏属性..不会对其变形!!

# {'_People__name': '小川', 'age': 20, 'sex': 'male', '__height': 173}
peo1.__height = 173
print(peo1.__dict__)
1
2
3

在继承那一小节,分析 '实例化对象查找属性的顺序'时, 举了以下这个例子:
验证上方底层原理的第4点 ...不想让子类的方法覆盖父类的... : 若想访问到的是A类里的func1,如何做??
采用双下划线开头的方式将方法设置为私有的!

class A:
    def func1(self):
        print('A.func1')

    def func2(self):
        print('A.func2')
        self.func1() # obj.func1()

class B(A):
    def func1(self):
        print('B.func1')

obj = B()
obj.func2()
"""
A.func2
B.func1
"""

# -- ( ̄O ̄;) 用封装将其变个形!!看似相同实则发生了变形.
# 子类隐藏父类不隐藏;父类隐藏子类不隐藏;都隐藏 ... 都能实现上方变更的需求.

class A:
    def __func1(self): # _A.__func1
        print('A.func1')

    def func2(self):
        print('A.func2')
        self.__func1() # self._A__func1  obj._A__func1
        
class B(A): 
    def __func1(self): # _B__func1
        print('B.func1')

obj = B()
obj.func2()
"""
A.func2
A.func1
"""

# -------------
class Foo:
    __x = 111  # _Foo__x
    
class Bar(Foo):
    __x = 222  # _Bar__x -- 没有覆盖的效果
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47

# ☆开发接口

# 隐藏数据属性

封装数据属性的目的/应用场景:
       首先定义属性的目的就是为了给类外部的使用者使用的.
       隐藏之后是 为了不让外部使用者直接使用 ,需要在类内部开辟一个接口.
       然后让类外部的使用者通过接口来间接地操作隐藏的属性.

精髓在于 -- 我们可以在接口之上附加任意的逻辑,从而严格控制使用者对属性的操作 !'增删改查'

class People:

    def __init__(self, name, age):
        self.__name = name
        self.__age = age

    def tell_info(self):
        print(f'姓名:{self.__name},年龄:{self.__age}')

    def set_info(self, name, age):
        if type(name) is not str:
            print('用户名必须为str类型!')
            return
        if not isinstance(age, int):
            # 主动抛出一个错误让程序结束运行
            raise TypeError('年龄必须为整型!')

        self.__name = name
        self.__age = age


peo1 = People('egon', 18)
peo1.set_info('小川', 20)
peo1.tell_info()  # 姓名:小川,年龄:20
"""
封装数据属性并不是让使用者不用,只是不让他直接用,让其使用内部开发的接口实现间接使用,
而开发者可以在接口上添加逻辑严格控制使用者的操作行为
"""
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# 隐藏函数属性

封装函数属性的目的:
       首先定义属性的目的就是为了给类外部的使用者使用的.
       隐藏函数属性是为了不让外部使用者直接使用,需要类内部开辟一个接口.
       然后在接口内去调用隐藏的功能

精髓在于 --  隔离了复杂度!

栗子0: 电视机本身是个黑盒子,隐藏了所有细节,但是一定会对外提供一堆按钮,这些按钮也正是接口的概念 
栗子1: 快门就是傻瓜相机为傻瓜们提供的方法,该方法将内部复杂的照相功能都隐藏起来啦.

"""
取款是功能,而这个功能有很多功能组成:插卡、密码认证、输入金额、打印账单、取钱
对使用者来说,只需要知道取款这个功能即可,其余功能我们都可以隐藏起来
很明显这么做,隔离了复杂度,同时也提升了安全性
"""
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()

a = ATM()
a.withdraw()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

# @property

方法伪装数据属性

property装饰器用于将被装饰的方法 伪装成一个数据属性 ,在使用时可以不用加括号而直接使用.

class People:
    def __init__(self, name, weight, height):
        self.name = name
        self.weight = weight
        self.height = height
        # 不妥, 因为bmi指数应该是随着身高体重的变化而变化的
        # self.bmi = self.weight / (self.height**2)

    @property
    def bmi(self):
        return self.weight / (self.height**2)

peo1 = People('egon', 75, 1.8)
print(peo1.bmi)  # 23.148148148148145

# BMI指数听起来更像是一个特征(数据属性)而不是技能(函数属性).
# @property 将bmi这个技能伪装成了一个特征
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

结合封装的应用

与封装的属性 结合着 使用! >> 查看 - 修改 - 删除 <<

class People:

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

    @property
    def name(self):
        return f'姓名:{self.__name}'

    @name.setter  # 前提是name方法被property装饰过了.
    def name(self, name):
        if type(name) is not str:
            raise TypeError('名字必须为str类型..')
        self.__name = name

    @name.deleter
    def name(self):
        # del self.__name
        # raise PermissionError('不允许删除!')
        print('不允许删除!')


peo1 = People('egon')
print(peo1.name)  # 姓名:egon
peo1.name = '小川'
print(peo1.name)  # 姓名:小川

del peo1.name  # 不允许删除!

"""Ps 远古的用法,实现的效果是一样的.
class People:

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

    def get_name(self):
        return f'姓名:{self.__name}'

    def set_name(self, name):
        if type(name) is not str:
            raise TypeError('名字必须为str类型..')
        self.__name = name

    def del_name(self):
        print('不允许删除!')

    name = property(get_name, set_name, del_name)
    
peo1 = People('egon')
print(peo1.name)  # 姓名:egon
peo1.name = '小川'
"""
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52

多态与多态性
绑定与非绑定方法

← 多态与多态性 绑定与非绑定方法→

最近更新
01
deepseek本地部署+知识库
02-17
02
实操-微信小程序
02-14
03
教学-cursor深度探讨
02-13
更多文章>
Theme by Vdoing | Copyright © 2023-2025 DC | One Piece
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式