什么是封装

在程序设计中,封装(Encapsulation)是对具体对象的一种抽象,即将某些部分隐藏起来,在程序外部看不到,其

含义是其他程序无法调用。

  要了解封装,离不开“私有化”,就是将类或者是函数中的某些属性限制在某个区域之内,外部无法调用。

为什么要封装

  封装数据的主要原因是:保护隐私(把不想别人知道的东西封装起来)

  封装方法的主要原因是:隔离复杂度(比如:电视机,我们看见的就是一个黑匣子,其实里面有很多电器元件,对于

用户来说,我们不需要清楚里面都有些元件,电视机把那些电器元件封装在黑匣子里,提供给用户是一个遥控器,

通过遥控器就能实现对电视机的操作。)

python私有化

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的形式访问到.   print(A.__dict__)
# {'__module__': '__main__', '_A__N': 0, '__init__': <function A.__init__ at 0x00000000126B0E18>,
# '_A__foo': <function A.__foo at 0x00000000126B0EA0>,
# 'bar': <function A.bar at 0x00000000126B0F28>,
# '__dict__': <attribute '__dict__' of 'A' objects>, '__weakref__':
# <attribute '__weakref__' of 'A' objects>, '__doc__': None}

这种自动变形的特点:

    1、类中定义的__x只能在内部使用,如self.__x,引用的就是变形的结果。

    2、这种变形其实正是针对外部的变形,在外部是无法通过__x这个名字访问到的。

    3、在子类定义的__x不会覆盖在父类定义的__x,因为子类中变形成了:_子类名__x,而父类中变形成了:_父

类名__x,即双下滑线开头的属性在继承给子类时,子类是无法覆盖的。

这种变形需要注意的问题是:

  1、这种机制也并没有真正意义上限制我们从外部直接访问属性,知道了类名和属性名就可以拼出名字:_类名__属

性,然后就可以访问了,如a._A__N

2、变形的过程只在类的定义是发生一次,在定义后的赋值操作,不会变形

class A:
__N=0 #类的数据属性就应该是共享的,但是语法上是可以把类的数据属性设置成私有的如__N,会变形为_A__N def __init__(self):
self.__X = 10 # 变形为self._A__X a = A()
print(a._A__N) # 0
print(a._A__X) # 10
print(A._A__N) # 0
a.__N = 15 # 新增N的值,此时加__不会变形
print(a.__dict__) # 发现后面的N并没有变形
#{'_A__X': 10, '__N': 15}

  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 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

 

特性(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 = 10 #为特性area赋值
r1.perimeter = 15 #为特性perimeter赋值
r1.volume = 24 #为特性volume赋值
'''
抛出异常:
r1.area = 10 #第一个就抛异常了,后面的也一样
AttributeError: can't set attribute

为什么要用property

将一个类的函数定义成特性以后,对象再去使用的时候obj.name,根本无法察觉自己的name是执行了一个函数然后计算出来的,

这种特性的使用方式遵循了统一访问的原则

除此之外,看下

ps:面向对象的封装有三种方式:
【public】
这种其实就是不封装,是对外公开的
【protected】
这种封装方式对外不公开,但对朋友(friend)或者子类(形象的说法是“儿子”,但我不知道为什么大家 不说“女儿”,就像“parent”本来是“父母”的意思,但中文都是叫“父类”)公开
【private】
这种封装对谁都不公开
class Foo:
def __init__(self,val):
self.__NAME=val #将所有的数据属性都隐藏起来 @property
def name(self):
return self.__NAME #obj.name访问的是self.__NAME(这也是真实值的存放位置) @name.setter
def name(self,value):
if not isinstance(value,str): #在设定值之前进行类型检查
raise TypeError('%s must be str' %value)
self.__NAME=value #通过类型检查后,将值value存放到真实的位置self.__NAME @name.deleter
def name(self):
raise TypeError('Can not delete') f=Foo('egon')
print(f.name) # egon
f.name = 'alex' # 修改属性name = 'alex'
print(f.name)
# f.name=10 # 抛出异常'TypeError: 10 must be str'
del f.name # 抛出异常'TypeError: Can not delete'

  

封装与扩展性

封装在于明确区分内外,使得类实现者可以修改封装内的东西而不影响外部调用者的代码;而外部使用用者只知道一个接口(函数),

只要接口(函数)名、参数不变,使用者的代码永远无需改变。这就提供一个良好的合作基础——或者说,只要接口这个基础约定不变,则代码改变不足为虑。

#类的设计者
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("客厅","egon",100,50,50) #实例化一个对象r1
print(r1.area) #通过接口使用(area),使用者得到了客厅的面积
# -------------输出结果--------------
# 5000 #得到了客厅的面积

 扩展原有的代码,使功能增加: 

  

#类的设计者,轻松的扩展了功能,而类的使用者完全不需要改变自己的代码
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("客厅","alex",10,30,10) #实例化一个对象r1
print(r1.area) #通过接口使用(area),使用者得到了客厅的面积
# --------------输出结果---------------
# (300, 3000) #得到了新增的功能的值

参考资料

https://www.cnblogs.com/Michael--chen/p/6740455.html

最新文章

  1. okhttp封装时,提示 cannot resolve method OkHttpClient setConnectTimeout() 函数
  2. 几个简单的html+css+js题目
  3. cocos2dx中加入unzip
  4. Receving Transactions &gt; No data found IQC无法接收PO采购物料
  5. 用xml画水平虚线和竖直虚线.md
  6. C#使用RabbitMQ(转)
  7. 【CSS 第五天】背景,边框
  8. python迭代和解析(3):range、map、zip、filter和reduce函数
  9. java第六章异常
  10. .net core中的对象池
  11. 【387】Python format 格式化函数
  12. SQLSERVER NULL和空字符串的区别 使用NULL是否节省空间
  13. Win7下无法提交MapReduce Job到集群环境(转)
  14. Daily Srum 10.28
  15. ubuntu下 adb devices找不到devices
  16. 【python】常用的日期和时间操作
  17. android Service 的简单使用(转)
  18. 高性能Web服务器Nginx的配置与部署研究(8)核心模块之事件模块
  19. JDBC2.0操作:结果集,更新,插入,删除,批处理语句
  20. Mysql的transaction实现(转)

热门文章

  1. td里的英文字母不会自动换行的问题
  2. 2018.10.09 NOIP模拟 世界杯(图论+set优化)
  3. 2018.06.30 BZOJ 3932: [CQOI2015]任务查询系统(主席树)
  4. k8s容器挂载配置文件
  5. time &amp; datetime 模块
  6. java基础-day3
  7. new Date()之参数传递
  8. hdu 2845 Beans 2016-09-12 17:17 23人阅读 评论(0) 收藏
  9. 如何利用Visio设计一个系统的结构图
  10. WinRT 中检查 WiFi 是否可用