本篇文章仍然是面向对象的相关内容,主要分析了封装、反射、动态导入模块、类内置attr属性、包装与授权三大面向对象编程的后续处理方法
封装
封装的本质:
把方法放到类里、把类放到模块里就叫装;
而其他地方导入模块、引用类、调用方法时看不到对应的模块、类、方法的具体逻辑,只知道名字就可以,因为它们是隐藏着的,这就叫封
封装的本质就要明确区分内外,因此封装是一种思想,内部就是内部,外部就是外部
class People:
#以_开头的变量就是被隐藏的变量,外部使用它的时候就无法看到它
_place= 'Beijing'
__position= 'Python'
def __init__(self,id_num,name,age,salary):
self.id_num= id_num
self.name= name
self.age= age
self.salary= salary
def get_id(self):
print('I am a privacy,我需要找的id为【%s】' %self.id_num)
#实例化
v1= People('12345678','Zoro',22,2000000)
print(v1._place)
#虽然被隐藏了,但是仍然可以调用它,隐藏只是一种约定,但python没有真正限制你去访问
#但是__开头的,就必须带上类名
#print(v1.__position)
print(v1._People__position
运行结果:
真正效果上的封装: 明确区分内外,内部的实现逻辑,外部无法知道,并且为封装到内部的逻辑提供一个访问接口给外部使用
反射
反射的本质:
也叫做程序的自省,主要指程序可以访问、检测和修改它本身状态的一种能力
class BadGuy:
feature= 'Ugly' #特征是丑
def __init__(self,name,address): #两个参数,一个名字一个地址
self.name = name
self.address= address
#动作(函数属性)
def sell_house(self):
print('【%s】 正在卖房子'%self.name)
def rent_house(self):
print('【%s】 正在租房子' %self.name)
#实例化
p1= BadGuy('黑中介','沙河')
#p1.sell_hourse()
#开始自我反省(反射)
print(hasattr(p1,'name')) #对象中是否有name这个属性,返回值为布尔值
print(hasattr(p1,'sell_house'))
#hasattr()反射函数第一个参数为一个对象,第二个是字符串,相当于p1.name
print(getattr(p1,'name'))
#返回name这个属性对应的值(函数属性则返回函数内存地址),
#一参为对象,二参为字符串.若不存在,会报错
print(getattr(p1,'rent_house'))
#第三个参数为Default,表示若不存在则输出第三个参数
print(getattr(p1,'r_house','该属性不存在'))
setattr(p1,'新世界第五皇','路飞') #在属性字典里新增一个键值对,也就是多一个属性
print(p1.__dict__)
#也可以增加函数属性
setattr(p1,'add_num',lambda x:x+1) #自增1的匿名函数
setattr(p1,'join_str',lambda self:self.name+'是BadGuy') #拼接字符串的匿名函数
print(p1.add_num(1313)) #调用函数
print(p1.join_str(p1)) #因为是以self为的匿名函数,所以传入的参数为对象名
delattr(p1,'新世界第五皇') #删除属性字典里的键值对,一参为对象名,二参为键
print(p1.__dict__)
运行结果:
动态导入模块
首先自定义一个简单的模块,放到web文件夹组成一个包:
#from web import test #正常导入web包内的test自定义模块
#动态导入模块
module = __import__('web.test') #导入相对路径下的web包内的test模块
print(module) #其实导入的只是web包,整个module就代表了web,并没有test1
#这样操作才可以导入web下的test1
module.test.test1() #导入并调用test1函数
#利用importlib模块来导入
import importlib
m = importlib.import_module('web.test')
print(m) #与__module__不同,这里导入的就是test这个模块了
#因此可以直接调用
m.test2() #就不用m.test.test2了
运行结果:
类内置attr属性
总共有三大类内置attr属性:
getattr
setattr
delattr
class Test:
y = 2020
def __init__(self,x):
self.x= x
def __getattr__(self,item): #item为必须参数
print('正在执行__getattr__方法')
def __delattr__(self,item):
print('正在触发__delattr__操作')
def __setattr__(self,key,value): #设置值的时候是加入属性字典里,所以传入的是键值对
print('正在用__setattr__设置值')
#__setattr__内必须有设置值的操作
self.__dict__[key]= value #设置原属性字典
#实例化
p1= Test(520)
#__getattr__方法
print(p1.x) #用属性字典来获取属性
print(getattr(p1,'__getattr__')) #用getattr函数来获取属性
p1.jjjjjjjjjjjjjjjjjj #一个不存在的属性名,就会触发__getattr__方法的执行
#__delatrr__方法
del p1.y #当执行del操作时就会触发__delattr__方法的执行
#p1.__dict__.pop('x') #这种方法不会触发__delattr__的执行
#__setattr__方法,当设置值的生活就会触发
print(p1.__dict__) #输出原属性字典
p1.y= 13 #又设置两个新的键值对,就会执行两次__setattr__
p1.z= 14
print(p1.__dict__)
运行结果:
__getattr__的好处:
当传入不存在的属性时不会报错,而是输出对应的提醒
class Test:
y = 2020
def __init__(self,x):
self.x= x
def __getattr__(self,item): #item为必须参数
print('你找的属性【%s】不存在!' %item) #此时的item就是调用时传入的属性
#实例化
p1= Test(520)
#__getattr__方法
print(p1.x) #用属性字典来获取属性
print(getattr(p1,'__getattr__')) #用getattr函数来获取属性
p1.jjjjjjjjjjjjjjjjjj #一个不存在的属性名,就会触发__getattr__方法的执行
运行结果:
包装与授权
包装: 包装一个类型通常是对已经存在的类型进行一些定制,可以新建、修改、删除原有产品的功能,其他的保持原样
授权: 授权是包装的一个特性,所有更新的功能都是由新类的某部分来处理,但已存在的功能就授权给对象的默认属性;实现授权的关键点就是覆盖__getattr__方法
import time #导入时间模块
class Open: #产生一个文件的类
def __init__(self,filename,mode,encoding):
#self.filename= filename
#打开一个文件并设为self.file属性
self.file= open(filename,mode,encoding=encoding)
self.mode= mode
self.encoding= encoding
#一个中转站
def __getattr__(self,item):
print('【%s】不存在!' %item)
print(item,type(item)) #输出传入值和它的类型
#授权的实现
return getattr(self.file, item)
#获取self.file中的信息,不存在就会新建一个,并返回新建方法的内存地址
#覆盖__getattr__方法,即自定义一个write的方法,就表示已经存在了write方法,
#所以不会触发__getattr__方法去新建一个write方法
def write(self,line): #第二个参数是传入的写的内容
t = time.strftime('%Y-%m-%d %t') #字符格式化显示系统时间,年-月-日
self.file.write(t + line) #字符串拼接
#实例化及调用
p1= Open('a.txt','r+','gbk')
print(p1.file) #输出file的信息
p1.read #触发__getattr__,运行getattr()方法
#新建了一个item的方法,就可以调用到了
print(p1.write) #再次触发__getattr__,使其新建一个write的方法
#就可以实现写的操作了
p1.write('Is ok...\n')
p1.write('终于可以写进来了\n')
p1.file.close() #关闭文件,实现数据的更新
运行结果:
在文件中也正常写入了内容: