「Python基础学习」之封装大法(封装python程序)
off999 2024-10-01 14:06 21 浏览 0 评论
在程序设计中,封装是对具体对象的一种抽象,简单来说就是将某些部分隐藏起来,在程序外部看不到,这个看不到不是说人用眼睛看不到那个代码,其含义是其它的程序无法调用。
要了解封装,离不开“私有化”,就是将类或者是函数中的某些属性限制在某个区域之内,外部无法调用。
封装
1、为什么要封装?
封装数据的主要原因是:保护隐私(把不想别人知道的东西封装起来)
封装方法的主要原因是:隔离复杂度(比如:电视机,我们看见的就是一个黑匣子,其实里面有很多电器元件,对于用户来说,我们不需要清楚里面都有些元件,电视机把那些电器元件封装在黑匣子里,提供给用户的只是几个按钮接口,通过按钮就能实现对电视机的操作。)
小提示:在编程语言里,对外提供的接口(接口可理解为了一个入口),就是函数,称为接口函数,这与接口的概念还不一样,接口代表一组接口函数的集合体。
2、封装分为哪几个层面?
封装其实分为两个层面,但无论哪种层面的封装,都要对外界提供好访问你内部隐藏内容的接口(接口可以理解为入口,有了这个入口,使用者无需且不能够直接访问到内部隐藏的细节,只能走接口,并且我们可以在接口的实现上附加更多的处理逻辑,从而严格控制使用者的访问)
第一个层面的封装(什么都不用做):创建类和对象会分别创建二者的名称空间,我们只能用类名.或者obj.的方式去访问里面的名字,这本身就是一种封装。
print(m1.brand) #实例化对象(m1.)
print(motor_vehicle.tag) #类名(motor_vehicle.)
-------------输出结果--------------
春风
fuel oil
注意:对于这一层面的封装(隐藏),类名.和实例名.就是访问隐藏属性的接口
第二个层面的封装:类中把某些属性和方法隐藏起来(或者说定义成私有的),只在类的内部使用、外部无法访问,或者留下少量接口(函数)供外部访问。
Python中私有化的方法也比较简单,即在准备私有化的属性(包括方法、数据)名字前面加两个下划线即可。
类中所有双下划线开头的名称如__x都会自动变形成:_类名__x的形式:
class A:
__N=0 #类的数据属性就应该是共享的,但是语法上是可以把类的数据属性设置成私有的如__N,会变形为_A__N
def __init__(self):
self.__X=10 #变形为self._A__X
def __foo(self): #变形为_A__foo
print('from A')
def bar(self):
self.__foo() #只有在类内部才可以通过__foo的形式访问到.
这种自动变形的特点:
1、类中定义的__x只能在内部使用,如self.__x,引用的就是变形的结果。
2、这种变形其实正是针对外部的变形,在外部是无法通过__x这个名字访问到的。
3、在子类定义的__x不会覆盖在父类定义的__x,因为子类中变形成了:_子类名__x,而父类中变形成了:_父
类名__x,即双下划线开头的属性在继承给子类时,子类是无法覆盖的。
注意:对于这一层面的封装(隐藏),我们需要在类中定义一个函数(接口函数)在它内部访问被隐藏的属性,然后外部就可以使用了
这种变形需要注意的问题是:
1.这种机制也并没有真正意义上限制我们从外部直接访问属性,知道了类名和属性名就可以拼出名字:_类名__属性,然后就可以访问了,如a._A__N
a = A()
print(a._A__N)
print(a._A__X)
print(A._A__N)
--------输出结果--------
0
10
0
2.变形的过程只在类的定义是发生一次,在定义后的赋值操作,不会变形
a = A() #实例化对象a
print(a.__dict__) #打印变形的内容
a.__Y = 20 #新增Y的值,此时加__不会变形
print(a.__dict__) #打印变形的内容
---------输出结果----------
{'_A__X': 10}
{'_A__X': 10, '__Y': 20} #发现后面的Y并没有变形
3.在继承中,父类如果不想让子类覆盖自己的方法,可以将方法定义为私有的
class A: #这是正常情况
def fa(self):
print("from A")
def test(self):
self.fa()
class B(A):
def fa(self):
print("from B")
b = B()
b.test()
--------输出结果----------
from B
看一下把fa被定义成私有的情况:
class A: #把fa定义成私有的,即__fa
def __fa(self): #在定义时就变形为_A__fa
print("from A")
def test(self):
self.__fa() #只会与自己所在的类为准,即调用_A__fa
class B(A):
def __fa(self): #b调用的是test,跟这个没关系
print("from B")
b = B()
b.test()
-------输出结果---------
from A
3、特性(property)
1.什么是特性property
property是一种特殊的属性,访问它时会执行一段功能(函数)然后返回值(就是一个装饰器)
注意:被property装饰的属性会优先于对象的属性被使用,而被propery装饰的属性,分成三种:property、被装饰的函数名.setter、被装饰的函数名.deleter(都是以装饰器的形式)。
class room: #定义一个房间的类
def __init__(self,length,width,high):
self.length = length #房间的长
self.width = width #房间的宽
self.high = high #房间的高
@property
def area(self): #求房间的平方的功能
return self.length * self.width #房间的面积就是:长x宽
@property
def perimeter(self): #求房间的周长的功能
return 2 * (self.length + self.width) #公式为:(长 + 宽)x 2
@property
def volume(self): #求房间的体积的功能
return self.length * self.width * self.high #公式为:长 x 宽 x 高
r1 = room(2,3,4) #实例化一个对象r1
print("r1.area:",r1.area) #可以像访问数据属性一样去访问area,会触发一个函数的执行,动态计算出一个值
print("r1.perimeter:",r1.perimeter) #同上,就不用像调用绑定方法一样,还得加括号,才能运行
print("r1.volume:",r1.volume) #同上,就像是把运算过程封装到一个函数内部,我们不管过程,只要有结果就行
------------输出结果---------------
r1.area: 6
r1.perimeter: 10
r1.volume: 24
注意:此时的特性arear、perimeter和volume不能被赋值。
r1.area = 8 #为特性area赋值
r1.perimeter = 14 #为特性perimeter赋值
r1.volume = 24 #为特性volume赋值
'''
抛出异常:
r1.area = 8 #第一个就抛异常了,后面的也一样
AttributeError: can't set attribute
'''
2、为什么要用property
将一个类的函数定义成特性以后,对象再去使用的时候obj.name,根本无法察觉自己的name是执行了一个函数然后计算出来的,这种特性的使用方式遵循了统一访问的原则。
class people: #定义一个人的类
def __init__(self,name,sex):
self.name = name
self.sex = sex #p1.sex = "male",遇到property,优先用property
@property #查看sex的值
def sex(self):
return self.__sex #返回正真存值的地方
@sex.setter #修改sex的值
def sex(self,value):
if not isinstance(value,str): #在设定值之前进行类型检查
raise TypeError("性别必须是字符串类型") #不是str类型时,主动抛出异常
self.__sex = value #类型正确的时候,直接修改__sex的值,这是值正真存放的地方
#这里sex前加"__",对sex变形,隐藏。
@sex.deleter #删除sex
def sex(self):
del self.__sex
p1 = people("egon","male") #实例化对象p1
print(p1.sex) #查看p1的sex,此时要注意self.sex的优先级
p1.sex = "female" #修改sex的值
print(p1.sex) #查看修改后p1的sex
print(p1.__dict__) #查看p1的名称空间,此时里面有sex
del p1.sex #删除p1的sex
print(p1.__dict__) #查看p1的名称空间,此时发现里面已经没有sex了
-------------------输出结果--------------------
male
female
{'name': 'egon', '_people__sex': 'female'}
{'name': 'egon'}
python并没有在语法上把它们三个内建到自己的class机制中,在C++里一般会将所有的所有的数据都设置为私有的,然后提供set和get方法(接口)去设置和获取,在python中通过property方法可以实现。
4、封装与扩展性
封装在于明确区分内外,使得类实现者可以修改封装内的东西而不影响外部调用者的代码;而外部使用用者只知道一个接口(函数),只要接口(函数)名、参数不变,使用者的代码永远无需改变。这就提供一个良好的合作基础——或者说,只要接口这个基础约定不变,则代码改变不足为虑。
#类的设计者
class room: #定义一个房间的类
def __init__(self,name,owner,length,width,high):
self.name = name
self.owner = owner
self.__length = length #房间的长
self.__width = width #房间的宽
self.__high = high #房间的高
@property
def area(self): #求房间的平方的功能
return self.__length * self.__width #对外提供的接口,隐藏了内部的实现细节,\
# 此时我们想求的是房间的面积就是:长x宽
#类的设计者
class room: #定义一个房间的类
def __init__(self,name,owner,length,width,high):
self.name = name
self.owner = owner
self.__length = length #房间的长
self.__width = width #房间的宽
self.__high = high #房间的高
@property
def area(self): #求房间的平方的功能
return self.__length * self.__width #对外提供的接口,隐藏了内部的实现细节,\
# 此时我们想求的是房间的面积就是:长x宽
实例化对象通过接口,调用相关属性得到想要的值:
#类的使用者
r1 = room("客厅","michael",20,30,9) #实例化一个对象r1
print(r1.area) #通过接口使用(area),使用者得到了客厅的面积
-------------输出结果--------------
600 #得到了客厅的面积
扩展原有的代码,使功能增加:
#类的设计者,轻松的扩展了功能,而类的使用者完全不需要改变自己的代码
class room: #定义一个房间的类
def __init__(self,name,owner,length,width,high):
self.name = name #房间名
self.owner = owner #房子的主人
self.__length = length #房间的长
self.__width = width #房间的宽
self.__high = high #房间的高
@property
def area(self): #对外提供的接口,隐藏内部实现
return self.__length * self.__width,\
self.__length * self.__width * self.__high #此时我们增加了求体积,
# 内部逻辑变了,只需增加这行代码就能简单实现,而且外部调用感知不到,仍然使
# 用该方法,但是功能已经增加了
对于类的使用者,仍然在调用area接口的人来说,根本无需改动自己的代码,就可以用上新功能:
#类的使用者
r1 = room("客厅","michael",20,30,9) #实例化一个对象r1
print(r1.area) #通过接口使用(area),使用者得到了客厅的面积
--------------输出结果---------------
(600, 5400) #得到了新增的功能的值
结尾
最后多说一句,小编是一名python开发工程师,这里有我自己整理了一套最新的python系统学习教程,包括从基础的python脚本到web开发、爬虫、数据分析、数据可视化、机器学习等。想要这些资料的可以关注小编,并在后台私信小编:“01”即可领取。
相关推荐
- 面试官:来,讲一下枚举类型在开发时中实际应用场景!
-
一.基本介绍枚举是JDK1.5新增的数据类型,使用枚举我们可以很好的描述一些特定的业务场景,比如一年中的春、夏、秋、冬,还有每周的周一到周天,还有各种颜色,以及可以用它来描述一些状态信息,比如错...
- 一日一技:11个基本Python技巧和窍门
-
1.两个数字的交换.x,y=10,20print(x,y)x,y=y,xprint(x,y)输出:102020102.Python字符串取反a="Ge...
- Python Enum 技巧,让代码更简洁、更安全、更易维护
-
如果你是一名Python开发人员,你很可能使用过enum.Enum来创建可读性和可维护性代码。今天发现一个强大的技巧,可以让Enum的境界更进一层,这个技巧不仅能提高可读性,还能以最小的代价增...
- Python元组编程指导教程(python元组的概念)
-
1.元组基础概念1.1什么是元组元组(Tuple)是Python中一种不可变的序列类型,用于存储多个有序的元素。元组与列表(list)类似,但元组一旦创建就不能修改(不可变),这使得元组在某些场景...
- 你可能不知道的实用 Python 功能(python有哪些用)
-
1.超越文件处理的内容管理器大多数开发人员都熟悉使用with语句进行文件操作:withopen('file.txt','r')asfile:co...
- Python 2至3.13新特性总结(python 3.10新特性)
-
以下是Python2到Python3.13的主要新特性总结,按版本分类整理:Python2到Python3的重大变化Python3是一个不向后兼容的版本,主要改进包括:pri...
- Python中for循环访问索引值的方法
-
技术背景在Python编程中,我们经常需要在循环中访问元素的索引值。例如,在处理列表、元组等可迭代对象时,除了要获取元素本身,还需要知道元素的位置。Python提供了多种方式来实现这一需求,下面将详细...
- Python enumerate核心应用解析:索引遍历的高效实践方案
-
喜欢的条友记得关注、点赞、转发、收藏,你们的支持就是我最大的动力源泉。根据GitHub代码分析统计,使用enumerate替代range(len())写法可减少38%的索引错误概率。本文通过12个生产...
- Python入门到脱坑经典案例—列表去重
-
列表去重是Python编程中常见的操作,下面我将介绍多种实现列表去重的方法,从基础到进阶,帮助初学者全面掌握这一技能。方法一:使用集合(set)去重(最简单)pythondefremove_dupl...
- Python枚举类工程实践:常量管理的标准化解决方案
-
本文通过7个生产案例,系统解析枚举类在工程实践中的应用,覆盖状态管理、配置选项、错误代码等场景,适用于Web服务开发、自动化测试及系统集成领域。一、基础概念与语法演进1.1传统常量与枚举类对比#传...
- 让Python枚举更强大!教你玩转Enum扩展
-
为什么你需要关注Enum?在日常开发中,你是否经常遇到这样的代码?ifstatus==1:print("开始处理")elifstatus==2:pri...
- Python枚举(Enum)技巧,你值得了解
-
枚举(Enum)提供了更清晰、结构化的方式来定义常量。通过为枚举添加行为、自动分配值和存储额外数据,可以提升代码的可读性、可维护性,并与数据库结合使用时,使用字符串代替数字能简化调试和查询。Pytho...
- 78行Python代码帮你复现微信撤回消息!
-
来源:悟空智能科技本文约700字,建议阅读5分钟。本文基于python的微信开源库itchat,教你如何收集私聊撤回的信息。[导读]Python曾经对我说:"时日不多,赶紧用Python"。于是看...
- 登录人人都是产品经理即可获得以下权益
-
文章介绍如何利用Cursor自动开发Playwright网页自动化脚本,实现从选题、写文、生图的全流程自动化,并将其打包成API供工作流调用,提高工作效率。虽然我前面文章介绍了很多AI工作流,但它们...
- Python常用小知识-第二弹(python常用方法总结)
-
一、Python中使用JsonPath提取字典中的值JsonPath是解析Json字符串用的,如果有一个多层嵌套的复杂字典,想要根据key和下标来批量提取value,这是比较困难的,使用jsonpat...
你 发表评论:
欢迎- 一周热门
- 最近发表
- 标签列表
-
- python计时 (73)
- python安装路径 (56)
- python类型转换 (93)
- python自定义函数 (53)
- python进度条 (67)
- python吧 (67)
- python字典遍历 (54)
- python的for循环 (65)
- python格式化字符串 (61)
- python串口编程 (60)
- python读取文件夹下所有文件 (59)
- java调用python脚本 (56)
- python操作mysql数据库 (66)
- python字典增加键值对 (53)
- python获取列表的长度 (64)
- python接口 (63)
- python调用函数 (57)
- python人脸识别 (54)
- python多态 (60)
- python命令行参数 (53)
- python匿名函数 (59)
- python打印九九乘法表 (65)
- python赋值 (62)
- python异常 (69)
- python元祖 (57)