面向对象高级
1.反射
1.1反射的定义
反射机制指的是在程序的运行状态中
对于任意一个类,都可以知道这个类的所有属性和方法;
对于任意一个对象,都能够调用他的任意方法和属性。
这种动态获取程序信息以及动态调用对象的功能称为反射机制。
1.2为何使用反射
- 好处一:实现可插拔机制
可以事先定义好接口,接口只有在被完成后才会真正执行,这实现了即插即用,这其实是一种‘后期绑定’,即可以事先把主要的逻辑写好(只定义接口),然后后期再去实现接口的功能
- 好处二:动态导入模块(基于反射当前模块成员)
1.3如何实现反射
class People:
def __init__(self,name,age):
self.name=name
self.age=age
def say(self):
print('<%s:%s>' %(self.name,self.age))
obj=People('csm',18)
实现反射机制的步骤
1.先通过内置函数dir来获取任意一个类或者对象的属性列表,列表中全为字符串格式
print(dir(obj))
#['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'age', 'name', 'say']
2.可以通过字符串反射到真正的属性上,得到属性值
print(obj.__dict__[dir(obj)[-2]])#csm
四个内置函数的使用:通过字符串来操作属性值
1.hasattr() :检测是否含有某属性
判断object中有没有一个name字符串对应的方法或属性
print(hasattr(obj,'name')) #True
print(hasattr(obj,'x')) #False
2.getattr:获取属性
print(getattr(obj,'name')) #csm
print(getattr(obj,'sex',None)) # 等同于obj.sex,不存在该属性则返回默认值None
3.setattr:设置属性
setattr(obj,'name','cll')
print(obj.name) #cll
4.delattr:删除属性
delattr(obj,'name')
print(obj.__dict__)#{'age': 18}
obj=10
if hasattr(obj,'x'):
print(getattr(obj,'x',None))
else:
pass
if hasattr(obj,'x'):
print(setattr(obj,'x',111))
else:
pass
class Ftp:
def put(self):
print('正在执行上传功能')
def get(self):
print('正在执行下载功能')
def interactive(self):
method=input(">>>: ").strip() # method='put'
if hasattr(self,method):
getattr(self,method)()
else:
print('输入的指令不存在')
obj=Ftp()
obj.interactive()
2.内置方法
2.1什么是内置方法
定义在类内部,以__开头并以__结果的方法
特点:会在某种情况下自动触发执行
2.2 为何要用内置方法
为了定制化我们的类or对象
2.3 如何使用内置方法
#__str__:在打印对象时会自动触发,然后将返回值(必须是字符串类型)当做本次打印的结果输出
class People:
def __init__(self, name, age):
self.name = name
self.age = age
def __str__(self):
# print('运行了...')
return "<%s:%s>" %(self.name,self.age)
obj = People('csm', 18)
# print(obj.__str__())
print(obj) # <csm:18>
#__del__:在清理对象时触发,会先执行该方法
#由于Python自带的垃圾回收机制会自动清理Python程序的资源,所以当一个对象只占用应用程序级资源时,完全没必要为对象定制__del__方法,但在产生一个对象的同时涉及到申请系统资源(比如系统打开的文件、网络连接等)的情况下,关于系统资源的回收,Python的垃圾回收机制便派不上用场了,需要我们为对象定制该方法,用来在对象被删除时自动触发回收系统资源的操作
class People:
def __init__(self, name, age):
self.name = name
self.age = age
self.x = open('a.txt',mode='w')
# self.x = 占据的是操作系统资源
def __del__(self):
# print('run...')
# 发起系统调用,告诉操作系统回收相关的系统资源
self.x.close()
obj = People('辣白菜同学', 18)
# del obj # obj.__del__()
print('============>')
3.元类
一切都源自于一句话:一切皆为对象
3.1什么是元类
元类就是用来实例化产生类的类
关系:元类---实例化---->类(People)---实例化---->对象(obj)
class People:
def __init__(self,name,age):
self.name=name
self.age=age
def say(self):
print('%s:%s' %(self.name,self.name))
print(People.__dict__)
#{'__module__': '__main__', '__init__': <function People.__init__ at 0x0000025B2469B790>, 'say': <function People.say at 0x0000025B2474FDC0>, '__dict__': <attribute '__dict__' of 'People' objects>, '__weakref__': <attribute '__weakref__' of 'People' objects>, '__doc__': None}
# 如何得到对象:所有的对象都是实例化或者说调用类而得到的(调用类的过程称为类的实例化)
# obj=调用类()
obj=People('egon',18)
print(type(obj)) #<class '__main__.People'>
# 如果说类也是对象
# People=调用类(。。。)
# 查看内置的元类:
# 1、type是内置的元类
# 2、我们用class关键字定义的所有的类以及内置的类都是由元类type实例化产生
print(type(People)) #<class 'type'>
print(type(int)) #<class 'type'>
3.2class关键字创建类的流程分析
用class关键字定义的类本身也是一个对象,负责产生该对象的类称之为元类(元类可以简称为类的类),内置的元类为type
类有三大特征:
# 1、类名
class_name="People"
# 2、类的基类
class_bases=(object,)
# 3、执行类体代码拿到类的名称空间
class_dic={}
class_body="""
def __init__(self,name,age):
self.name=name
self.age=age
def say(self):
print('%s:%s' %(self.name,self.name))
"""
exec(class_body,{},class_dic)
# print(class_dic)
# 4、调用元类
People=type(class_name,class_bases,class_dic)
exec的用法:
#exec:三个参数
#参数一:包含一系列python代码的字符串
#参数二:全局作用域(字典形式),如果不指定,默认为globals()
#参数三:局部作用域(字典形式),如果不指定,默认为locals()
#可以把exec命令的执行当成是一个函数的执行,会将执行期间产生的名字存放于局部名称空间中
g={
'x':1,
'y':2
}
l={}
exec('''
global x,z
x=100
z=200
m=300
''',g,l)
print(g) #{'x': 100, 'y': 2,'z':200,......}
print(l) #{'m': 300}
3.3 如何自定义元类来控制类的产生
一个类没有声明自己的元类,默认他的元类就是type,除了使用内置元类type,我们也可以通过继承type来自定义元类,然后使用metaclass关键字参数为一个类指定元类
class Mymeta(type): # 只有继承了type类的类才是元类
# 空对象,"People",(),{...}
def __init__(self,class_name,class_bases,class_dic):
print('run22222222222....')
print(self)
if not class_name.istitle():
raise NameError('类名的首字母必须大写啊!!!')
if class_name.islower():
raise TypeError('类名%s请修改为驼峰体' % class_name)
if '__doc__' not in class_dic or len(class_dic['__doc__'].strip(' \n')) == 0:
raise TypeError('类中必须有文档注释,并且文档注释不能为空')
# 当前所在的类,调用类时所传入的参数
def __new__(cls, *args, **kwargs):
# 造Mymeta的对象
print('run1111111111.....')
# print(cls,args,kwargs)
# return super().__new__(cls,*args, **kwargs)
return type.__new__(cls,*args, **kwargs)
# People=Mymeta("People",(object,),{...})
# 调用Mymeta发生三件事,调用Mymeta就是type.__call__
# 1、先造一个空对象=>People,调用Mymeta类内的__new__方法
# 2、调用Mymeta这个类内的__init__方法,完成初始化对象的操作
# 3、返回初始化好的对象
class People(metaclass=Mymeta):
"""
文档注释
"""
def __init__(self,name,age):
self.name=name
self.age=age
def say(self):
print('%s:%s' %(self.name,self.name))
#run1111111111.....
run22222222222....
<class '__main__.People'>
# 强调:只要是调用类,那么会一次调用
# 1、类内的__new__
# 2、类内的__init__
3.4自定义元类控制类People的调用=》类的对象的产生
# 知识储备:__call__
class Foo:
def __call__(self, *args, **kwargs):
print(self)
print(args)
print(kwargs)
return 123
obj=Foo()
#1、要想让obj这个对象变成一个可调用的对象,需要在该对象的类中定义一个方法__call__方法,该方法会在调用对象时自动触发
#2、调用obj的返回值就是__call__方法的返回值
res=obj(1,2,3,x=1,y=2)
print(res) #123
# 应用:如果想让一个对象可以加括号调用,需要在该对象的类中添加一个方法__call__
# 总结:
# 对象()->类内的__call__
# 类()->自定义元类内的__call__
# 自定义元类()->内置元类__call__
class Mymeta(type): # 只有继承了type类的类才是元类
def __call__(self, *args, **kwargs):
# 1、Mymeta.__call__函数内会先调用People内的__new__
people_obj=self.__new__(self)
# 2、Mymeta.__call__函数内会调用People内的__init__
self.__init__(people_obj,*args, **kwargs)
# print('people对象的属性:',people_obj.__dict__)
people_obj.__dict__['xxxxx']=11111
# 3、Mymeta.__call__函数内会返回一个初始化好的对象
return people_obj
# 类的产生
# People=Mymeta()=》type.__call__=>干了3件事
# 1、type.__call__函数内会先调用Mymeta内的__new__
# 2、type.__call__函数内会调用Mymeta内的__init__
# 3、type.__call__函数内会返回一个初始化好的对象
class People(metaclass=Mymeta):
def __init__(self,name,age):
self.name=name
self.age=age
def say(self):
print('%s:%s' %(self.name,self.name))
def __new__(cls, *args, **kwargs):
# 产生真正的对象
return object.__new__(cls)
# 类的调用
# obj=People('egon',18) =》Mymeta.__call__=》干了3件事
# 1、Mymeta.__call__函数内会先调用People内的__new__
# 2、Mymeta.__call__函数内会调用People内的__init__
# 3、Mymeta.__call__函数内会返回一个初始化好的对象
obj1=People('egon',18)
obj2=People('egon',18)
# print(obj)
print(obj1.__dict__)
print(obj2.__dict__)
3.5属性查找
属性查找的原则:对象-》类-》父类
切记:父类 不是 元类
class Mymeta(type):
n=444
def __call__(self, *args, **kwargs): #self=<class '__main__.StanfordTeacher'>
obj=self.__new__(self) # StanfordTeacher.__new__
# obj=object.__new__(self)
print(self.__new__ is object.__new__) #True
self.__init__(obj,*args,**kwargs)
return obj
class Bar(object):
# n=333
# def __new__(cls, *args, **kwargs):
# print('Bar.__new__')
pass
class Foo(Bar):
# n=222
# def __new__(cls, *args, **kwargs):
# print('Foo.__new__')
pass
class StanfordTeacher(Foo,metaclass=Mymeta):
# n=111
def __init__(self,name,age):
self.name=name
self.age=age
obj=StanfordTeacher('lili',18)
print(obj.__dict__)
# print(obj.n)
# print(StanfordTeacher.n)