反射、自定义内置方法来定制类的功能、元类
一、反射
1. 定义:通过字符串来操作类或者对象属性
2. 方法:hasattr、getattr、setattr、delattr
3. 使用方法:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 class People: 2 def __init__(self,name): 3 self.name=name 4 def put(self): 5 print('%s is putting'%self.name) 6 def get(self): 7 print('%s get sth'%self.name) 8 def run(self): 9 while True: 10 choice=input('>>').strip() 11 method=getattr(obj,choice,None) 12 13 # # 判断对象obj里边有没有choice这个属性 14 # print(hasattr(obj,choice)) 15 16 # # obj.choice=z 17 # setattr(obj,choice,name) 18 19 # # delete boj.msg 20 # delattr(obj,msg) 21 if method is None: 22 print('command is not exists') 23 else: 24 # getattr 是取得字符串并将该字符串转成可执行的函数 25 method() 26 27 obj=People('大王') 28 obj.run()
4.反射的好处:可以事先定义好接口,接口只有在被完成后才会真正执行,这实现了即插即用,这其实是一种‘后期绑定’,什么意思?即你可以事先把主要的逻辑写好(只定义接口),然后后期再去实现接口的功能
二、自定义内置方法来实现定制类的功能
1. __str__
class People: def __init__(self,name,age): self.name=name self.age=age # 在打印对象时会自动触发这个功能 # 此时应该收集跟这个对象有关的信息,输出到屏幕上 def __str__(self): return '<%s : %s>'%(self.name,self.age) p=People('小天使',17) print(p)
2.__del__析构方法
class People: def __init__(self,name): self.name=name self.f=open('a.txt','rt',encoding='utf8') # 会在对象删除之前自动触发 def __del__(self): # print('>>>>>>>') # 做回收系统资源相关的事情 self.f.close() p=People('魔鬼') print('主')
三、元类
1.定义
在python中一切都是对象,所以用class 关键字定义的类本身也是一个对象,负责产生该对象的类称之为元类,即产生类的类即称为元类。
2.为何用元类
元类是负责产生类的,所以学习元类或者自定义元类的目的是为了控制类的产生过程,还可以控制对象的产生过程。
知识准备:
内置函数exec的用法:将函数体代码执行一遍
# 将函数体代码执行一遍,放至局部{}中 msg=''' x=1 def func(): pass ''' class_dic={} exec(msg,{},class_dic) print(class_dic)
__call__的用法
如果想要让实例化出来的对象变成可以调用的对象,则需要在该对象的类中定义一个__call__的方法,该对象会在调用时自动触发
class Foo: def __call__(self, *args, **kwargs): print(self) print(args) print(kwargs) obj=Foo() obj() obj(1,2,3,x=1,y=2)
执行结果入下图
3. 创建类的方法有两种
如果说类也是对象的话,那么用class关键字去创建类的过程也是一个实例化的过程
该实例化的目的是为了得到一个类,调用的是元类
3.1 方式一:用默认的元类type
class People: def __init__(self,name): self.name=name def eat(self): print('%s is eating...'%self.name) print(type(People))
3.1.1 创建类的三个要素 (类名,基类们,类的名称空间)
class_name='People' #类名 class_bases=(object,) #基类 class_dic={} #类的名称空间 class_body=''' #类体代码 class People: def __init__(self,name): self.name=name def eat(self): print('%s is eating...'%self.name) print(type(People)) ''' exec(class_body,{},class_dic)
3.2 用的自定义的元类
class Mymeta(type): # Mymeta=type(...) def __init__(self,class_name,class_bases,class_dic): super(Mymeta, self).__init__(class_name,class_bases,class_dic) class People(object,metaclass=Mymeta): # People=Mymeta(类名,基类们,类的名称空间) def __init__(self,name): self.name=name def eat(self): print('%s is eating...'%self.name) p=People('小三炮') p.eat()
原理:
3.2.1 得到一个字符串的类名 class_name = 'People'
3.2.2 得到一个类的基类们 class_bases = (object,)
3.3.3 执行类体代码,得到一个类的名称空间 class_dic = {...}
3.3.4 调用 People = type(class_name,class_bases,class_dic)
3.3 应用
自定义元类控制类的产生过程,类的产生过程其实就是元类的调用过程
class Mymeta(type): def __init__(self,class_name,class_bases,class_dic): super(Mymeta, self).__init__(class_name,class_bases,class_dic) print(class_dic) # {'__module__': '__main__', '__qualname__': 'People', '__doc__': ' ', # '__init__': <function People.__init__ at 0x000001FCE6048AE8>} print(class_name) # People print(class_bases) # (<class 'object'>,) if class_dic.get('__doc__') is None and len(class_dic.get('__doc__'))==0: raise TypeError('Document must exists and must not be None') if not class_name.istitle(): raise NameError('First letter must be capital') class People(object,metaclass=Mymeta): # if class_name'initials is not capital,it # will report an error ''' hey guys ''' def __init__(self,name): self.name=name p=People('明日之星')
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 class Mymeta(type): 2 3 4 def __call__(self, *args, **kwargs): 5 6 # 先造出一个类的空对象 7 obj=self.__new__(self) 8 9 # 为该空对象初始化独有属性 10 self.__init__(obj,*args,**kwargs) 11 12 # 返回一个初始化好的对象 13 return obj 14 15 16 class People(object,metaclass=Mymeta): 17 def __init__(self,name): 18 print(self) 19 self.name=name 20 21 def eat(self): 22 print('%s is eating'%self.name) 23 def __new__(cls, *args, **kwargs): #(涉及查找顺序) 24 print(cls) # <class '__main__.People'> 25 # cls.__new__(cls) # 错误 26 obj=super(People,cls).__new__(cls) 27 return obj 28 obj=People('大宝',)
3.4 元类中的属性查找顺序
1.先查找对象的层次
2.对象层次中若没有,则从元类的层次中寻找
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 # 将类的属性更改 2 class Mymeta(type): 3 def __init__(self,class_name,class_bases,class_dic): 4 # 控制类Foo的创建 5 super(Mymeta, self).__init__(class_name,class_bases,class_dic) 6 7 def __call__(self, *args, **kwargs): 8 # 控制类Foo的调用过程,即控制类Foo的产生过程 9 obj=self.__new__(self) 10 self.__init__(obj,*args,**kwargs) 11 obj.__dict__={'_%s__%s'%(self.__name__,k):v for k,v in obj.__dict__.items()} 12 # 必须有返回值,否则报AttributeError: 'NoneType' object has no attribute '__dict__' 13 return obj 14 15 class Foo(object,metaclass=Mymeta): 16 def __init__(self,name): 17 self.name=name 18 19 obj=Foo('读书郎') 20 print(obj.__dict__)