面向对象高级
1.反射
反射是所有动态语言比较重要的一个特征 , 提一嘴go
, 虽然go是静态的 , 但是也支持反射 , 这是go比较优秀的一点 , python一切皆对象的设计保证了实现反射非常简单
1.1什么是反射机制
反射机制指的是在程序的运行状态中
对于任意一个类,都可以知道这个类的所有属性和方法;
对于任意一个对象,都能够调用他的任意方法和属性。
这种动态获取程序信息以及动态调用对象的功能称为反射机制。
反射 : 指的是在程序运行过程中可以"动态(不见棺材不掉泪)"获取对象的信息
补充 :
任何语言都需要指定类型 , 但你会发现 x = 10 , 没有指定类型啊
在python中当解释器执行到这一句话的时候 , 才会自动根据值 指定类型
1.2为什么要用反射
你函数接收到一个参数以后 , 你有可能要访问这个参数内部的一些属性 , 但是你不确定是否有这个属性 , 这样你就要先判断 , 如果有了 , 再访问 , 但是这样判断比较low , 所以我们使用反射来做
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('辣良菜同学')
# 实现反射机制的步骤
# 1、先通过多din:查看出某一个对象下可以.出哪些属性来
print(dir(obj))
# 2、可以通过字符串反射到真正的属性上,得到属性值
print(obj.__dict__[dir(obj)[-2]])
# 这样些确实实现了反射 , 但是不够优雅 , python给我们专门提供了四个内置函数
四个内置函数的使用 : 通过字符串来操作属性值
# hasattr() , 见名知意 , 我就不解释了
print(hasattr(obj,'name')) # 返回bool值
print(hasattr(obj,'x'))
# getattr()
print(getattr(obj, 'name' )) # 获取name属性值
print(getattr(obj, 'name1' ,None)) # 当获取不到属性值 , 设置返回值为None
# setattr()
setattr(obj,'name','EGON') # obj. name="EGON"
# delattr()
delattr(obj, 'name' ) # del obj.name
print(obj.__dict__)
# 一般hasattr()和getattr() 搭配使用
2.内置方法
即双下方法 , 在类中定义了这类方法 , 满足某种条件下会自动触发 , 不需要调用
为什么要用内置方法呢? ---> 就是为了定制化我们的类or对象
关于内置方法的学习 , 我们只需要知道 , 在什么情况下会自动触发这个方法
__str__
方法
# __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('辣白菜同学', 18)
print(obj) # print(obj.__str__())
__del__
方法
# __del__:在清理对象时触发,会先执行该方法
class People:
def __init__(self,name,age):
self.name = name
self.age = age
self.x= open('a.txt',mode='w')
#sef.x=占据的是操作系统资源
def __del__(self):
# print( 'run....')
# 发起系统调用,告诉操作系统回收相关的系统资源
self.x.close()
obj = People('alex',99)
# del obj obj.__del__()
print('===========')
其他的内置方法还有很多 , 就不一 一介绍了
详情请见 : https://www.cnblogs.com/linhaifeng/articles/6204014.html
3.元类
3.1 什么是元类
一切源自于一句话:python中一切皆为对象。让我们先定义一个类,然后逐步分析
class StanfordTeacher(object):
school='Stanford'
def __init__(self,name,age):
self.name=name
self.age=age
def say(self):
print('%s says welcome to the Stanford to learn Python' %self.name)
所有的对象都是实例化或者说调用类而得到的(调用类的过程称为类的实例化),比如对象t1是调用类StanfordTeacher得到的
t1=StanfordTeacher('lili',18)
print(type(t1)) #查看对象t1的类是<class '__main__.StanfordTeacher'>
如果一切皆为对象,那么类StanfordTeacher本质也是一个对象,既然所有的对象都是调用类得到的,那么StanfordTeacher必然也是调用了一个类得到的,你可能会疑问我StanfordTeacher类 , 不是使用class机制得到的吗 , 实际上任何关键字机制后面 , 都对应了一系列的代码 , 这个代码做的实际上就是造一个类 , 那么类是对象 , 肯定也是通过另一个类加括号实例化的 , 这个类就是元类
元类就是用来实例化产生类的类 , 前提是我们把类当成对象去看待
实例化-->类( People )--实例化---对象(obj) , 那么如何查看呢?
# 查看内置的元类 我们用 class关键字定义的类以及内置的类都是由内置type帮我们实例化产生的# type是内置的元类print(type(People)) # <class 'type'>print(type(int)) # <class 'type'>
3.2class机制分析
一起分析一下 , class关键字是怎么把类给造出来的?
# class关键字创造类 People的步骤# 类有三大特征# 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.age))"""exec(class_body, {} , class_dic)#print(class_dic)# 4.调用元类# print(type (class_name ,cLass_bases , class _dic))People=type (class_name ,cLass_bases , class _dic)
3.3如何自定元类来控制类的产生
class Mymeta(type): # 只有继承了type类的类才是元类(目前先不考虑type类是怎么造的) # 空对象, " People", (object,),{...} def __init__(self, x, y, z): # print(self) # print(x) # print(y) # print(z) if not x.capitalize(): raise NameError('类名的首字母必须大写') def __new__(cls, *args, **kwargs): # 造Mymeta的对象 # super().__new__(cls, *args, **kwargs) or type.__new__(cls, *args, **kwargs) # 必须返回一个空对象 return type.__new__(cls, *args, **kwargs)# People=Mymeta("People",(object,),{...})# 调用Mymeta发生三件事 , 调用Mymeta就是type.__call__# 1、先造一个空对象=> People# 2、调用Mymeta这个类内的__init__方法,完成初始化对象的操作# 3、返回初始化好的对象class People(metaclass=Mymeta): # 默认是type def __init__(self, name, age): self.name = name self.age = age def say(self): print('<%s: %s>' % (self.name, self.age)) """强调只要是调用类,那么会一次调用1、类内的__new__2、类内的__init__"""
还有一个返回初始化对象没介绍 , 思考一下 , 调用Mymeta能不能说就是调用__new__
, 不能因为__init__
也被调用了 , 而且还有调用顺序 , 实际上调用Mymeta , 是调用__call__
方法
``call`方法
class Foo: def __init__(self, x, y): self.x = x self.y = y def __call__(self,*args,**kwargs): print('===2>1')obj = Foo(111, 222)print(obj) # print(obj.__str__())res = obj() # res = obj.__call__()print(res)# 应用 : 如果想让一个对象可以加括号调用,需要在该对象的类中添加一个方法__call__
class Mymeta(type): # 只有继承了type类的类才是元类(目前先不考虑type类是怎么造的) # 空对象, " People", (object,),{...} def __init__(self, x, y, z): # print(self) # print(x) # print(y) # print(z) if not x.capitalize(): raise NameError('类名的首字母必须大写') def __new__(cls, *args, **kwargs): # 造Mymeta的对象 # super().__new__(cls, *args, **kwargs) or type.__new__(cls, *args, **kwargs) # 必须返回一个空对象 return type.__new__(cls, *args, **kwargs)# People=Mymeta("People",(object,),{...})# 调用Mymeta发生三件事 , 调用Mymeta就是type.__call__# 1、先造一个空对象=> People# 2、调用Mymeta这个类内的__init__方法,完成初始化对象的操作# 3、返回初始化好的对象class People(metaclass=Mymeta): # 默认是type def __init__(self, name, age): self.name = name self.age = age def say(self): print('<%s: %s>' % (self.name, self.age))"""对象()->类内的caLL类()->自定义元类内的__call__自定义元类()->内置元类__call__"""
模板 :
class Mymeta(type): #只有继承了type类才能称之为一个元类,否则就是一个普通的自定义类 def __call__(self, *args, **kwargs): #self=<class '__main__.StanfordTeacher'> #1、调用__new__产生一个空对象obj obj=self.__new__(self) # 此处的self是类OldoyTeacher,必须传参,代表创建一个StanfordTeacher的对象obj #2、调用__init__初始化空对象obj self.__init__(obj,*args,**kwargs) #3、返回初始化好的对象obj return objclass StanfordTeacher(object,metaclass=Mymeta): school='Stanford' def __init__(self,name,age): self.name=name self.age=age def say(self): print('%s says welcome to the Stanford to learn Python' %self.name)t1=StanfordTeacher('lili',18)print(t1.__dict__) #{'name': 'lili', 'age': 18}
上例的__call__
相当于一个模板,我们可以在该基础上改写__call__
的逻辑从而控制调用StanfordTeacher的过程,比如将StanfordTeacher的对象的所有属性都变成私有的
class Mymeta(type): #只有继承了type类才能称之为一个元类,否则就是一个普通的自定义类 def __call__(self, *args, **kwargs): #self=<class '__main__.StanfordTeacher'> #1、调用__new__产生一个空对象obj obj=self.__new__(self) # 此处的self是类StanfordTeacher,必须传参,代表创建一个StanfordTeacher的对象obj #2、调用__init__初始化空对象obj self.__init__(obj,*args,**kwargs) # 在初始化之后,obj.__dict__里就有值了 obj.__dict__={'_%s__%s' %(self.__name__,k):v for k,v in obj.__dict__.items()} #3、返回初始化好的对象obj return objclass StanfordTeacher(object,metaclass=Mymeta): school='Stanford' def __init__(self,name,age): self.name=name self.age=age def say(self): print('%s says welcome to the Stanford to learn Python' %self.name)t1=StanfordTeacher('lili',18)print(t1.__dict__) #{'_StanfordTeacher__name': 'lili', '_StanfordTeacher__age': 18}
3.4属性查找
元类不是父类
class Mymeta(type): n=444 def __call__(self, *args, **kwargs): #self=<class '__main__.StanfordTeacher'> obj=self.__new__(self) print(self.__new__ is object.__new__) #Trueclass Bar(object): n=333 # def __new__(cls, *args, **kwargs): # print('Bar.__new__')class Foo(Bar): n=222 # def __new__(cls, *args, **kwargs): # print('Foo.__new__')class StanfordTeacher(Foo,metaclass=Mymeta): n=111 school='Stanford' def __init__(self,name,age): self.name=name self.age=age def say(self): print('%s says welcome to the Stanford to learn Python' %self.name) # def __new__(cls, *args, **kwargs): # print('StanfordTeacher.__new__')StanfordTeacher('lili',18) #触发StanfordTeacher的类中的__call__方法的执行,进而执行self.__new__开始查找
总结,Mymeta下的__call__
里的self.__new__
在StanfordTeacher、Foo、Bar里都没有找到__new__
的情况下,会去找object里的__new__
,而object下默认就有一个__new__
,所以即便是之前的类均未实现__new__
,也一定会在object中找到一个,根本不会、也根本没必要再去找元类Mymeta->type中查找__new__
我们在元类的__call__
中也可以用object.__new__(self)
去造对象
但我们还是推荐在__call__
中使用self.__new__(self)
去创造空对象,因为这种方式会检索三个类StanfordTeacher->Foo->Bar,而object.__new__
则是直接跨过了他们三个
最后说明一点
class Mymeta(type): #只有继承了type类才能称之为一个元类,否则就是一个普通的自定义类 n=444 def __new__(cls, *args, **kwargs): obj=type.__new__(cls,*args,**kwargs) # 必须按照这种传值方式 print(obj.__dict__) # return obj # 只有在返回值是type的对象时,才会触发下面的__init__ return 123 def __init__(self,class_name,class_bases,class_dic): print('run。。。')class StanfordTeacher(object,metaclass=Mymeta): #StanfordTeacher=Mymeta('StanfordTeacher',(object),{...}) n=111 school='Stanford' def __init__(self,name,age): self.name=name self.age=age def say(self): print('%s says welcome to the Stanford to learn Python' %self.name)print(type(Mymeta)) #<class 'type'># 产生类StanfordTeacher的过程就是在调用Mymeta,而Mymeta也是type类的一个对象,那么Mymeta之所以可以调用,一定是在元类type中有一个__call__方法# 该方法中同样需要做至少三件事:# class type:# def __call__(self, *args, **kwargs): #self=<class '__main__.Mymeta'># obj=self.__new__(self,*args,**kwargs) # 产生Mymeta的一个对象# self.__init__(obj,*args,**kwargs) # return obj