Python面向对象三要素-封装(Encapsulation)
Python面向对象三要素-封装(Encapsulation)
作者:尹正杰
版权声明:原创作品,谢绝转载!否则将追究法律责任。
一.封装概述
将数据和操作组织到类中,即属性和方法
将数据隐藏起来,给使用者提供操作(方法)。使用者通过操作就可以获取或者修改数据。getter和setter。
通过访问控制,暴露适当的数据和操作给用户,该隐藏的隐藏起来,例如保护成员或私有成员。
二.类属性的访问控制
1>.抛出问题
#!/usr/bin/env python
#_*_conding:utf-8_*_
#@author :yinzhengjie
#blog:http://www.cnblogs.com/yinzhengjie class Person:
def __init__(self,name,age=18):
self.name = name
self.age = age def growup(self,i=1):
if i > 0 and i < 150: #控制逻辑
self.age += i p1 = Person("jason")
print(p1.age) p1.growup(20) #在我们控制逻辑的范围内
print(p1.age) p1.age = 9999 #直接修改对象的属性,超过了范围,并绕过了咱们控制逻辑,是不是很操蛋?Python提供了私有属性可以解决这个问题。
print(p1.age) #以上代码输出结果如下:
18
38
9999
2>.私有(Private)属性
#!/usr/bin/env python
#_*_conding:utf-8_*_
#@author :yinzhengjie
#blog:http://www.cnblogs.com/yinzhengjie class Person:
def __init__(self,name,age=18):
self.name = name
"""
私有变量的本质: 类定义的时候,如果声明一个实例变量的时候,使用双下划线,Python解释器会将其改名,转换名称为"_类名__变量名"的名称,所以用原来的名字访问不到了。
"""
self.__age = age #使用双下划线开头的属性名,就是私有属性 def growup(self,i=1):
if i > 0 and i < 150: #控制逻辑
self.__age += i def getage(self): #我们只对外提供访问"__age"的方法
return self.__age p1 = Person("jason")
print(p1.getage())
print(p1.__dict__) p1.growup(120)
print(p1.getage()) print(Person.__dict__)
print(p1.__dict__) #p1.__age == 9999 #我们发现现在无法访问到"__age"这个属性啦,会抛出"AttributeError"异常
p1._Person__age = 9999 #既然我们知道了私有变量的新名称,就可以直接从外部访问到,并修改它。因此尽管是私有属性我们依旧是可以对其进行更改,但建议大家不要去修改,因为这样就违背了私有变量但属性啦,但你如果一定要改的话你得知道去哪改哟。
print(p1.getage())
print(p1.__dict__) #以上代码输出结果如下:
18
{'name': 'jason', '_Person__age': 18}
138
{'__module__': '__main__', '__init__': <function Person.__init__ at 0x10075f950>, 'growup': <function Person.growup at 0x10075fae8>, 'getage': <function Person.getage at 0x10075fb70>, '__dict__': <attribute '__dict__' of 'Person' objects>, '__weakref__': <attribute '__weakref__' of 'Person' objects>, '__doc__': None}
{'name': 'jason', '_Person__age': 138}
9999
{'name': 'jason', '_Person__age': 9999}
3>.保护(protected)属性
#!/usr/bin/env python
#_*_conding:utf-8_*_
#@author :yinzhengjie
#blog:http://www.cnblogs.com/yinzhengjie class Person:
def __init__(self,name,age=18):
self.name = name
"""
在变量名前使用一个下划线,称为保护变量。
可以看出,这个_age属性根本就没有改变名称,和普通的属性一样,解释器不做任何特殊处理。
这只是开发者共同的约定,看见这种变量,就如同私有变量,不要直接使用。
"""
self._age = age p1 = Person("jason") print(p1._age)
print(p1.__dict__) # #以上代码输出结果如下:
18
{'name': 'jason', '_age': 18}
4>.私有方法
#!/usr/bin/env python
#_*_conding:utf-8_*_
#@author :yinzhengjie
#blog:http://www.cnblogs.com/yinzhengjie """
私有方法的本质
单下划线的方法只是开发者之间的约定,解释器不做任何改变。
双下划线的方法,是私有方法,解释器会改名,改名策略和私有变量相同, 即"_类名__方法名" 。
方法变量都在类的 __dict__ 中可以找到。
"""
class Person:
def __init__(self, name, age=18):
self.name = name
self._age = age """
参照保护变量、私有变量,使用单下划线、双下划线命名方法。
"""
def _getname(self):
return self.name def __getage(self):
return self._age jason = Person('Jason')
print(jason._getname()) # 没改名
#print(jason.__getage()) # 无此属性
print(jason.__dict__)
print(jason.__class__.__dict__)
print(jason._Person__getage()) # 改名了 #以上代码执行结果如下:
Jason
{'name': 'Jason', '_age': 18}
{'__module__': '__main__', '__init__': <function Person.__init__ at 0x10215f950>, '_getname': <function Person._getname at 0x10215fae8>, '_Person__getage': <function Person.__getage at 0x10215fb70>, '__dict__': <attribute '__dict__' of 'Person' objects>, '__weakref__': <attribute '__weakref__' of 'Person' objects>, '__doc__': None}
18
5>.私有成员的总结
在Python中使用 _单下划线 或者 __ 双下划线来标识一个成员被保护或者被私有化隐藏起来。
但是,不管使用什么样的访问控制,都不能真正的阻止用户修改类的成员。Python中没有绝对的安全的保护成员或者私有成员。 因此,前导的下划线只是一种警告或者提醒,请遵守这个约定。除非真有必要,不要修改或者使用保护成员或者私有成员,更不要修改它们。
三.补丁
可以通过修改或者替换类的成员。使用者调用的方式没有改变,但是,类提供的功能可能已经改变了。
猴子补丁(Monkey Patch):
在运行时,对属性、方法、函数等进行动态替换。
其目的往往是为了通过替换、修改来增强、扩展原有代码的能力。
黑魔法,慎用。
#!/usr/bin/env python
#_*_conding:utf-8_*_
#@author :yinzhengjie
#blog:http://www.cnblogs.com/yinzhengjie class Person:
def get_score(self):
#模拟下面的字典是从数据库拿的某个学生成绩(基本是及格的不多呀)。
ret = {"English":37,"Chinese":66,"History":52}
return ret
test2.py
#!/usr/bin/env python
#_*_conding:utf-8_*_
#@author :yinzhengjie
#blog:http://www.cnblogs.com/yinzhengjie def get_score(self):
return dict(name=self.__class__.__name__,English=98, Chinese=96, History=95)
test3.py
#!/usr/bin/env python
#_*_conding:utf-8_*_
#@author :yinzhengjie
#blog:http://www.cnblogs.com/yinzhengjie from test2 import Person
from test3 import get_score def monkey_patch_for_Person():
Person.get_score = get_score monkey_patch_for_Person() #打补丁操作 if __name__ == '__main__':
print(Person().get_score()) #以上代码输出结果如下:
{'name': 'Person', 'English': 98, 'Chinese': 96, 'History': 95}
四.属性装饰器
一般好的设计是:
把实例的某些属性保护起来,不让外部直接访问,外部使用getter读取属性和setter方法设置属性。
1>.自定义getter和setter方法
#!/usr/bin/env python
#_*_coding:utf-8_*_
#@author :yinzhengjie
#blog:https://www.cnblogs.com/yinzhengjie
#EMAIL:y1053419035@qq.com class Person:
def __init__(self,name,age=18):
self.name = name
self.__age = age """
我们自定义age和set_age方法操作属性,的确可以实现私有变量的管理。那有没有简单的方式呢?
"""
def age(self):
return self.__age def set_age(self,age):
self.__age = age jason = Person("Jason")
print(jason.age()) jason.set_age(20)
print(jason.age()) #以上代码输出结果如下:
18
20
2>.property装饰器
# !/usr/bin/env python
# _*_coding:utf-8_*_
# @author :yinzhengjie
# blog:https://www.cnblogs.com/yinzhengjie
# EMAIL:y1053419035@qq.com class Person:
def __init__(self, name, age=18):
self.name = name
self.__age = age """
特别注意:
使用property装饰器的时候这三个方法同名。
property装饰器必须在前,setter,deleter装饰器在后。
property装饰器能通过简单的方式,把对方法的操作变成对属性的访问,并起到了一定隐藏效果。 property装饰器:
后面跟的函数名是以后的属性名。它就是getter。这个必须有,有了它至少是只读属性。 setter装饰器:
与属性名同名,且接收2个参数,第一个self,第二个是将要赋值的值。有了它,属性可写。 deleter装饰器:
可以控制是否删除属性,很少用,咱们了解即可。
""" @property
def age(self):
return self.__age @age.setter
def age(self, age):
self.__age = age @age.deleter
def age(self):
del self.__age
print("del") jason = Person("Jason")
print(jason.age) jason.age += 10
print(jason.age) del jason.age # 以上代码输出结果如下:
18
28
del
#!/usr/bin/env python
#_*_coding:utf-8_*_
#@author :yinzhengjie
#blog:https://www.cnblogs.com/yinzhengjie
#EMAIL:y1053419035@qq.com class Person:
def __init__(self,name,age=18):
self.name = name
self.__age = age def getage(self):
return self.__age def setage(self,age):
self.__age =age def delage(self):
del self.__age
print("del") age = property(getage,setage,delage,"age property") jason = Person("Jason")
print(jason.age) jason.age = 20
print(jason.age) del jason.age #以上代码输出结果如下:
18
20
del
property的另外一种写法(第二种写法)
#!/usr/bin/env python
#_*_coding:utf-8_*_
#@author :yinzhengjie
#blog:https://www.cnblogs.com/yinzhengjie
#EMAIL:y1053419035@qq.com class Person:
def __init__(self,name,age=18):
self.name = name
self.__age = age age = property(lambda self:self.__age) #这种写法只能设置只读属性,无法让属性可写。 jason = Person("Jason")
print(jason.age) #以上代码输出结果如下:
18
仅设置只读的装饰器
五.对象的销毁
#!/usr/bin/env python
#_*_coding:utf-8_*_
#@author :yinzhengjie
#blog:https://www.cnblogs.com/yinzhengjie
#EMAIL:y1053419035@qq.com import time class Person:
def __init__(self,name,age=18):
self.name = name
self.__age = age """
类中可以定义"__del__"方法,称为析构函数(方法)。
作用:销毁类的实例的时候调用,以释放占用资源。其中就放些清理资源的代码,比如释放连接。
注意这个方法不能引起真正销毁,只是对象销毁的时候会自动调用它。 由于Python实现了引用计数的垃圾回收机制,不能确定对象何时执行垃圾回收。
我们可以使用del语句删除实例,引用计数减1。当引用计数为0时,会自动调用"__del__"方法。
由于垃圾回收对象销毁时,才会真正清理对象,还会在回收对象之前自动调用"__del__"方法,除非你明确自己的目的,建议不要手动调用这个方法。
"""
def __del__(self):
print("delete {}".format(self.name)) if __name__ == '__main__':
jason = Person("Jason")
jason.__del__()
jason.__del__()
jason.__del__()
jason.__del__()
print("=========start=========")
jason2 = jason
jason3 = jason2
print(1,"del")
del jason
time.sleep(2) print(2,"del")
del jason2
time.sleep(2)
print("*" * 50) del jason3 #注释一下看看效果
time.sleep(3)
print("=========end========") #以上代码执行结果如下:
delete Jason
delete Jason
delete Jason
delete Jason
=========start=========
1 del
2 del
**************************************************
delete Jason
=========end========
六.方法重载(overload)
其他面向对象的高级语言中,会有重载的概念。
所谓重载,就是同一个方法名,但是形式参数个数、类型不一样,就是同一个方法的重载。
Python没有重载!
Python不需要重载! Python中,方法(函数)定义中,形参非常灵活,不需要指定类型(就算指定了也只是一个说明而非约束),参数个数也不固定(可变参数)。一个函数的定义可以实现很多种不同形式实参的调用。所以Python不需要方法的重载。
或者说Python语法本身就实现了其它语言的重载。
Python面向对象三要素-封装(Encapsulation)的更多相关文章
- Python面向对象三要素-继承(Inheritance)
Python面向对象三要素-继承(Inheritance) 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.继承概述 1>.基本概念 前面我们学习了Python的面向对象三 ...
- Python面向对象三要素-多态
Python面向对象3要素-多态 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.多态概述 OCP原则:多用“继承”,少修改. 继承的用途:在子类上实现对基类的增强,实现多态. ...
- Python面向对象(三)
一.绑定方法与非绑定方法 一.绑定方法:绑定给谁就应该由谁来调用,谁来调用就会将谁当作第一个参数传入 1.绑定给对象的方法:类中定义的函数默认就是绑定给对象的 2.绑定给类的方法:为类中定义的函数加上 ...
- python 面向对象三大特性(封装 多态 继承)
今天我们来学习一种新的编程方式:面向对象编程(Object Oriented Programming,OOP,面向对象程序设计)注:Java和C#来说只支持面向对象编程,而python比较灵活即支持面 ...
- Python()- 面向对象三大特性----封装
封装: [封装] 隐藏对象的属性和实现细节,仅对外提供公共访问方式.[好处] 1. 将变化隔离: 2. 便于使用:3. 提高复用性: 4. 提高安全性:[封装原则] 1. 将 ...
- Python 面向对象三(转载)
来源:Mr.Seven www.cnblogs.com/wupeiqi/p/4766801.html 四.类的特殊成员 上文介绍了Python的类成员以及成员修饰符,从而了解到类中有字段.方法和属性三 ...
- python 面向对象三 访问权限 下划线 双下划线
一.双下划线 如果要让内部属性不被外部访问,可以把属性的名称前加上两个下划线__,在Python中,实例的变量名如果以__开头,就变成了一个私有变量(private),只有内部可以访问,外部不能访问. ...
- python面向对象中的封装、继承、多态
封装 可以简单的理解为隐藏一切可以隐藏的实现细节,只向外界提供简单的编程接口.我们在类中定义的方法其实就是把数据和数据的操作封装起来了,在我们创建了对象之后,只需要给对象发送一个消息(调用方法)就可以 ...
- python 面向对象(三)类与类之间的关系 初始化方法一些类
###################总结################# 面试的时候 让写python一些特殊方法 __init__ 创建对象的时候初始化 __new__对象实例化调用第一个方法 ...
随机推荐
- git清除master分支所有commit
原理:新切一个分支并切换到这个分支,删除原来的master分支,再将新分支变成master 步骤: 1. 创建并切换到新分支 git checkout --orphan latest_branch 2 ...
- Idea导入maven项目没有识别
选中module的pom.xml,右键,选择" add as maven project",idea会识别该pom的项目
- OpenShift 3.11离线环境的jenkins演示
离线安装完成后,一般情况下只装了个基础环境,catalog镜像没有导入,本文主要侧重在jenkins的一些环境设置和演示. 1.导入镜像 首先follow下面链接下载镜像 https://docs.o ...
- nginx入门系列之应用场景介绍
目录 HTTP服务器 反向代理服务器 作为一个虚拟主机下多个应用的反向代理 作为多个虚拟主机的反向代理 负载均衡器 简单轮训策略 最小连接数策略 客户端IP哈希策略 服务器权重策略 邮件代理服务器 官 ...
- Xamarin Assets文件读取
在Assets文件夹中添加nlog.config文件,在属性中将Build Action设置为AndroidAsset var steam = Assets.Open("nlog.confi ...
- Java之输入和输出
输出 在前面的代码中,我们总是用System.out.println()来向屏幕输出一些内容: println是print line的缩写,表示输出并换行.因此,如果输出后不想换行,可以用print( ...
- (转) C#使用ODP.NET(Oracle.ManagedDataAccess.dll)操作Oracle数据库
原贴链接:https://www.cnblogs.com/mq0036/p/11052359.html C#使用ODP.NET(Oracle.ManagedDataAccess.dll)操作Oracl ...
- 启动Oracle 12c数据库实例
启动Oracle 12c数据库实例 启动Oracle数据库实例,主要分为两步:第一步,启动监听:第二步,启动数据库实例. 1. 切换到oracle用户- su oracle- cd - source ...
- solr添加中文IK分词器,以及配置自定义词库
Solr是一个基于Lucene的Java搜索引擎服务器.Solr 提供了层面搜索.命中醒目显示并且支持多种输出格式(包括 XML/XSLT 和 JSON 格式).它易于安装和配置,而且附带了一个基于H ...
- ASP.NET Core 中间件Diagnostics使用 异常和错误信息
ASP.NET Core 中间件(Middleware)Diagnostics使用.对于中间件的介绍可以查看之前的文章ASP.NET Core 开发-中间件(Middleware). Diagnost ...