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__对象实例化调用第一个方法 ...
随机推荐
- Flask 学习(三)路由介绍
Flask路由规则都是基于Werkzeug的路由模块的,它还提供了很多强大的功能. 两种添加路由的方式 方式一: @app.route('/xxxx') # @decorator def index( ...
- shell基础知识5-函数
函数的定义 function fname(){ } 或者 function_name(){ } 对于简单的函数,甚至可以是这样做 fname() { statement; } 函数调用 直接写函数名即 ...
- springboot:自定义缓存注解,实现生存时间需求
需求背景:在使用springbot cache时,发现@cacheabe不能设置缓存时间,导致生成的缓存始终在redis中. 环境:springboot 2.1.5 + redis 解决办法:利用AO ...
- Xamarin Assets文件读取
在Assets文件夹中添加nlog.config文件,在属性中将Build Action设置为AndroidAsset var steam = Assets.Open("nlog.confi ...
- mvn-dependencies-vs-dependencyManagement
dependencyManagement里只是声明依赖,并不实现引入,因此子项目需要显式的声明需要用的依赖. dependencies 相对于dependencyManagement,所有声明在dep ...
- ssh_exchange_identification: read: Connection reset by peer 解决思路
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.csdn.net/Jdk_yxs/article/deta ...
- linux查看端口常用命令
netstat命令参数: -t : 指明显示TCP端口 -u : 指明显示UDP端口 -l : 仅显示监听套接字(所谓套接字就是使应用程序能够读写与收发通讯协议(protocol)与资料的程序) -p ...
- 【转帖】Infor转型十年启示录:ERP套件厂商为什么要做云平台?
Infor转型十年启示录:ERP套件厂商为什么要做云平台? https://www.tmtpost.com/4199274.html 好像浪潮国际 就是用的infor的ERP软件. 秦聪慧• 2019 ...
- QT 学习基础问题记录
1. connect 函数 需要先创建发送者和接收者实例,并且信号函数和槽函数如果有参数,需要在 connect 函数使用时指定相关参数类型. 2.窗口控件设置 设置窗口的最大化.最小化.问号提示等控 ...
- TCP/IP详解 IP路由选择
TCP/IP详解 IP路由选择 在本篇文章当中, 将通过例子来说明IP路由选择器过程 如图所示, 主机A与主机B分别是处在两个不同的子网当中, 中间通过一个路由连接. 如果主机A请求与主机B进行通行, ...