1.反射
1.1什么是反射
python是一门动态语言,而反射机制被视为动态语言的关键!
反射机制指的是:在程序的运行过程中,动态的获取程序的信息和对象的功能!
‘动态’:指一开始不知道程序的信息和对象的功能,只有等到运行到那的时候才会动态获取!!!
比如:x=18
在程序运行的时候,python才会通过反射机制动态的获取到这个值是整型,并不需要一开始定义的时候,就规定这个18位整型!
1.2为什么要用反射机制
当我们获取一个对象时,很多场景下,我们是并不知道这个对象里面是有什么属性和方法的,我们需要通过反射机制,动态的获取到该对象的属性和方法!!
案例:当我们在接收用户输入指令的时候,我们接收的是一个用户输入的字符串类型的指令,我们需要通过反射机制判断,这个字符串是不是该对象的功能,如果是调用该功能,如果不是返回提示信息!
案例代码化:
class Func:
def put(self):
print('正在执行上传功能')
def get(self):
print('正在执行下载功能')
# 该函数是用来接收用户的操作指令,并判断该对象是否有该功能,有则执行,没有则提示没有该功能
def action(self):
action = input('请输入操作指令:')
if hasattr(self,action):
getattr(self,action)()
else:
print('没有该功能')
obj = Func()
obj.action()
1.3如何实现反射机制
其实就是四个内置函数的使用!
class People:
def __init__(self,name):
self.name = name
def say(self):
pass
obj = People('zhang')
# 1.可以通过dir方法获取obj对象有哪些属性
print(dir(obj)) # 格式是一个列表套字符串的形式
# 2.通过字符串反射到真正的属性上,从而得到属性,操作属性
# 四个内置函数的使用
print(hasattr(obj,'name')) # hasattr()判断obj这个对象有没有name这个属性,name是字符串格式
print(getattr(obj,'name')) # 等同于obj.name
setattr(obj,'name','yang') # 等同于obj.name='yang'
delattr(obj,'name') # 等同于 del obj.name
print(obj.__dict__) # 结果为{}
# 上述四个方法也可以括号里放个类,判读类是否有该函数
res = getattr(People,'say') # 等同于People.say
print(res)
2.内置方法
2.1什么是内置方法
定义在类的内部,以__开头__结尾的方法
特点是在满足某种情况下自动触发该方法!!!!
2.2为毛要用内置方法
为了自定义定制我们的类or对象
2.3如何使用内置方法
2.3.1 __str__方法
class Func:
def put(self):
print('正在执行上传功能')
def __str__(self):
return 'w1e' # 打印对象是返回的值,必须位字符串类型
obj = Func()
print(obj) # 等同于print(obj.__str__())
# 不定义__str__方法,印出来为<__main__.Func object at 0x00000149DE206FA0>
# 定义__str__方法,可以在__str__内部函数指定返回的东西,return后面必须是字符串类型
2.3.2 __del__方法
该方法是在清理对象之前触发,会先执行该方法
class Func:
def put(self):
print('正在执行上传功能')
def __del__(self):
print('run...')
# 在del内部更多的是进行清理该对象占用系统的资源,对象清理了需要发起系统调用,清理对象占据的系统资源!
obj = Func()
del obj # 清理对象了,清理完之后会直接调用__del__方法,然后在执行下面的代码;如果不手动清理,在执行代码全部运行完之后,程序也会清理,打印run
print('====')
2.3.3 __call__方法
如果想要让一个对象可以加括号调用,需要在该对象的类中添加一个__call__方法
class Person(object):
def __init__(self,name):
self.name=name
def __call__(self, *args, **kwargs):
print(args,kwargs) #a()里传入的参数
return 123 # __call__的返回值就是a()的返回值
a = Person('zhang')
res = a(1,2,3,a=4,b=5) # 如果想要让对象a可以加括号调用,就必须在该对象的类中定义__call__方法,不添加则报错
print(res)
同样,如果想要类可以加括号调用,需要在该类的元类里添加一个__call__方法
调用__call__方法完成了三件事:
1.调用该类中的__new__方法造出一个空对象
2.调用该类中的__init__方法造出一个初始化对象(给上面的空对象添加属性,穿衣服)
3.返回初始化好的对象
2.4 总结
了解:这些内置方法__str__等又称魔法方法!
# __init__:类实例化会触发
# __str__:打印对象会触发
# __call__:对象()触发,类也是对象 类(),类的实例化过程调用元类的__call__
# __new__:在类实例化会触发,它比__init__早(造出裸体的人,__init__穿衣服)
# __del__:del 对象,对象回收的时候触发
# __setattr__,__getattr__:(.拦截方法),当对象.属性--》赋值会调用setattr,如果是取值会调用getattr
# __getitem__,__setitem__:([]拦截)
# __enter__和__exit__ 上下文管理器
上下文管理器应用:
class Person:
def __enter__(self):
print("我在with管理的时候,会触发")
print('进入with语句块时执行此方法,此方法如果有返回值会赋值给as声明的变量')
return 'oo'
def __exit__(self, exc_type, exc_val, exc_tb):
print('退出with代码块时执行此方法')
print('1', exc_type)
print('2', exc_val)
print('3', exc_tb)
with Person() as p: # 这句话执行,会触发类的__enter__
print(p)
小练习:类中可以通过对象.属性的方式获取值和设置值,但是不可以通过对象['键']=值设置值和获取值,思考如何可以实现呢?
class Person:
def __init__(self,name):
self.name = name
def __setitem__(self, key, value): # 重写类的__setitem__方法
setattr(self,key,value) # 当对象['键']=值触发该方法,调用setattr是触发self.key=value
def __getitem__(self, item): # 同上
return getattr(self,item)
p = Person('zy')
print(p.name)
p['name']=10
print(p['name'])
3.元类
3.1什么是元类
元类就是用来实例化产生类的类
关系:元类--->实例化--->类--->实例化--->对象(obj)
3.2如何查看内置的元类
其实,我们使用class定义的各种类和内置的类都是由内置的元类type帮我们实例化产生的
我们可以使用type()函数查看内置的元类
例如:在python中int、dict内置元类都继承自object类,int和dict又都是type元类的对象
print(type(int)) # <class 'type'>
print(type(dict)) # <class 'type'>
那么type和object又是什么关系呢?我们来type一下object和type!
print(type(type)) #<class 'type'>
print(type(object)) #<class 'type'>
其实:
1.object的元类其实是type类,object是由type类构造出来的对象
2.type是自己的对象(指针指向了自己)
3.type类又继承了object类
3.3class机制(class如何造出类的)
calss其实底层执行了以下四个步骤,造出了类!
class Func:
def put(self):
print('正在执行上传功能')
def __del__(self):
print('run...')
# 1.得到类名
class_name = 'Func'
# 2.得到类的基类
class_bases = (object,)
# 3.执行类体代码拿到名称空间!
class_dict = {}
class_body = """
def put(self):
print('正在执行上传功能')
def __del__(self):
print('run...')
"""
# exec第一个参数是类体代码、第二个是类体代码中的全局变量、第三的是一个空字典容器
exec (class_body,{},class_dict)
print(class_dict)
# 4.调用元类,得到People类
Func = type(class_name,class_bases,class_dict)
print(Func)
# Func类就是type元类实例化产生出来的对象!!!!
3.4如何自定义元类来控制类的产生
在3.3中,我们是使用type元类控制Func类的产生。其实,我们也可以自定义元类来控制类产生
class MyMeta(type): # 只有继承了type的类才是元类
def __init__(self,x,y,z): # 注意调用MyMeta这个类其实传入了四个参数分别是self、class_name,class_bases,class_dict
print('run...')
print(x) # x对应class_name
print(y) # y对应class_bases
print(z) # z对应class_dict
class Func(metaclass=MyMeta):
def put(self):
print('正在执行上传功能')
def __del__(self):
print('run...')
# 在自定义类的时候,metaclass默认等于type,我们可以通过指定metaclass=MyMeta来自定义元类
# 指定了metaclass=MyMeta,其实就执行了第四步调用元类Func = MyMeta(class_name,class_bases,class_dict)
# 调用MyMeta(class_name,class_bases,class_dict)发生了三件事!
# 注意!!!调用它就等于调用了type的__call__方法!!!!
# 1.先造一个空对象---Func--这里其实先调用了MyMeta类里的__new__()方法
# 2.调用MyMeta这个类的__init__方法,完成初始化对象操作
# 3.返回初始化好的对象
"""
完成上述操作之后,我们就可以在自定义的MyMeta元类里面的init方法里面,规定一下类的产生必须满足那些条件!
"""
3.5 元类下的属性查找
首先,切记!!父类不是元类!!
对象.属性查找是先从自己那找,再到类中,再到该类的父类中,最后到object类
类.属性查找是先从该类的父类中找,再到父类的父类中找,再到object中找,最后还要到该类的元类中找
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:面向对象高级–反射、内置方法和元类 - Python技术站