反射,元类
一 isinstance与issubstance:
isinstance判断的是obj是否是cls的对象:
class Foo: def __init__(self,name): self.name=name def tell(self): print('self') obj=Foo('egon') print(isinstance(obj,Foo))
issubstance判断的是子类与父类的关系与否:必须是类与类之间的比较
issubstance(Foo,object)
二 反射:
什么叫反射:python面向对象中的反射:通过字符串的形式操作对象相关的属性。python中的一切事物都是对象(都可以使用反射)
内置方法:
下面是反射的一些操作:hasattr,getattr,setattr,delattr:
class Foo: country='China' def __init__(self,name): self.name=name def tell(self): print('tell info') obj=Foo('egon') # print(hasattr(Foo,'country')) # print(hasattr(Foo,'tell')) # print(hasattr(obj,'country')) # print(hasattr(Foo,'tell')) # print(getattr(Foo,'country'))
print(getattr(obj,'tell',None)) #从obj中取‘tell’属性,如果没有的话会报错,所以我们为了防止报错应该在后面加上‘None’
setattr(Foo,'age',19) # 为Foo设置age=19
print(Foo.__dict__) # 类对象调用__dict__方法得到的是其下所有的属性以及对应的值。
delattr(Foo,'age') # 删除Foo中的属性age
print(Foo.__dict__)
下面我们说一种getattr与hasattr连用的方式:
class Foo: # def __init__(self): # while True: # cmd=input(':').strip() # if hasattr(self,cmd): # func=getattr(self,cmd) # func() # def download(self): # print('download...') # def upload(self): # print('upload....') #
三 内置的方法:__str__ ,__del__ ,__call__,__getattr__是一些满足条件自动触发。其中__getattr__是在对象调一个不存在的属性的时候执行的。
__str__所表达的意思就是:
在print打印类的对象的时候会自动触发,__str__下的代码运行。
class People: def __init__(self,name,age,sex): self.name=name self.age=age self.sex=sex def __str__(self): print('=====================================') return ''' name:%s age:%s sex:%s '''%(self.name,self.age,self.sex) obj=People('feng',18,'male') print(People)#
print(obj)#相当于打印print(obj.__dict__())
__del__:
一般用于回收系统资源,在内存中的对象被删除之前,会触发__del__先回收系统资源。
例1: import time class People: def __init__(self,name,age,sex): self.name=name self.age=age self.sex=sex def __del__(self):#在对象被删除的条件下,自动回收系统资源。 print('__del__') obj=People('eogn',18,'female') del obj time.sleep(5) 例2: class Mysql: # def __init__(self,ip,port): # self.ip=ip # self.port=port # self.conn=connect(ip,port) # 申请系统资源 # # def __del__(self): # self.conn.close() # # obj=Mysql('1.1.1.1',3306) class MyOpen: def __init__(self,filepath,mode="r",encoding="utf-8"): self.filepath=filepath self.mode=mode self.encoding=encoding self.fobj=open(filepath,mode=mode,encoding=encoding) def __str__(self): msg=""" filepath:%s mode:%s encoding:%s """ %(self.filepath,self.mode,self.encoding) return msg def __del__(self): self.fobj.close() # f=open('a.txt',mode='r',encoding='utf-8') f=MyOpen('aaa.py',mode='r',encoding='utf-8') # print(f.filepath,f.mode,f.encoding) # print(f) # print(f.fobj) res=f.fobj.read() print(res)
__call__:调用对象就会触发__call__,也就是在对象后面加括号()。
class Foo: def __init__(self): pass def __str__(self): return '324' def __del__(self): pass def __call__(self, *args, **kwargs): print('__call__',*args,**kwargs) obj=Foo() # print(obj) obj(1,2,3,a=2)
四 元类
元类:类的类就是元类
我们一般用class来定义一个类,从而实例化出一系列对象,
内置的元类type是用来专门产生class定义的类的。那么从这里我们可以看出类就是类型。
1 补充exec:
code=''' x=1 y=2 z=3 ''' global_dic={} local_dic={} 字符串中的代码没有声明全局,那么就是局部global x---》x=12 exec (code,{全局名称空间},{局部名称空间}) exec(code,global_dic,local_dic) exec会运行code的代码将产生的全局的名字放到全局名称空间 print(global_dic) print(local_dic) code=''' x=1 y=2 def f1(self,a,b): pass ''' local_dic={} exec(code,{},local_dic) print(local_dic)##{'x': 1, 'y': 2, 'f1': <function f1 at 0x000000FEFBB12E18>} exec是用来运行字符串中的代码,产生的名字放在名称空间中,如果声明了全局名称空间,那么就放到全局里面, 否则就默认为局部名称。
创建类的两种方式:
第一种:直接用class产生
class Chinese: country='China' def __init__(self,name,age): self.name=name self.age=age def talk(self): print('%s is talking' %self.name) print(type(Chinese))#<class 'type'>我们可以看出Chinese就是类,
第二种:手动模拟class创建类的过程,通过元类type来产生
type中有三个参数:在实例化的过程type(what,bases=None,dict=None)分别代表了:类名,类的基类,类的名称空间。也就是类的三要素。
类的组成:类名,类的基类,类的名称空间,其实就是在定义阶段产生的名字放在类所在的名称空间。 类名 class_name='Chinese' 类的基类 class_bases=(object,)#这里必须要加一个逗号’,‘ 类体 class_body=""" country='China' def __init__(self,name,age): self.name=name self.age=age def talk(self): print('%s is talking' %self.name) """ class_dic={} exec(class_body,globals(),class_dic)步骤一(先处理类体->名称空间):类体定义的名字都会存放于类的名称空间中(一个局部的名称空间), 我们可以事先定义一个空字典,然后用exec去执行类体的代码(exec产生名称空间的过程与真正的class过程类似,只是后者会将__开头的属性变形), 生成类的局部名称空间,即填充字典 print(class_dic)输出结果为{'country': 'China', 'talk': <function talk at 0x101a560c8>, '__init__': <function __init__ at 0x101a56668>} Foo=type(class_name,class_bases,class_dic) #实例化type得到对象Foo,即我们用class定义的类Foo,通过type得到的就是一个类, 跟我们用class来定义是一样的。 print(Foo) print(type(Foo)) print(isinstance(Foo,type)) # 得到的结果为: ''' <class '__main__.Chinese'> <class 'type'> True '''
我们还可以自定义一个元类type:
#4 、自定义元类: # class Mymeta(type): # # 来控制类Foo的创建 # def __init__(self,class_name,class_bases,class_dic): #self=Foo # # print(class_name) # # print(class_bases) # # print(class_dic) # if not class_name.istitle(): # raise TypeError('类名的首字母必须大写傻叉') # # if not class_dic.get('__doc__'): # raise TypeError('类中必须写好文档注释,大傻叉') # # super(Mymeta,self).__init__(class_name,class_bases,class_dic) # # # 控制类Foo的调用过程,即控制实例化Foo的过程 # def __call__(self, *args, **kwargs): #self=Foo,args=(1111,) kwargs={} # # print(self) # # print(args) # # print(kwargs) # # #1 造一个空对象obj # obj=object.__new__(self) # # #2、调用Foo.__init__,将obj连同调用Foo括号内的参数一同传给__init__ # self.__init__(obj,*args,**kwargs) # # return obj # # # # #Foo=Mymeta('Foo',(object,),class_dic) # class Foo(object,metaclass=Mymeta): # """ # 文档注释 # """ # x=1 # def __init__(self,y): # self.Y=y # # def f1(self): # print('from f1') # # # obj=Foo(1111) #Foo.__call__() # # # print(obj) # # print(obj.y) # # print(obj.f1) # # print(obj.x)
单例模式:也就是说当很多个实例化产生对象的结果是一样的情况下,我们可以使用单例模式,通过绑定@classmethod,让相同的对象都指向同一个地址,节省内存空间。
import settings class MySQL: __instance=None def __init__(self,ip,port): self.ip=ip self.port=port @classmethod def singleton(cls): if not cls.__instance: obj=cls(settings.IP, settings.PORT) cls.__instance=obj return cls.__instance obj1=MySQL('1.1.1.2',3306) obj2=MySQL('1.1.1.3',3307) obj3=MySQL('1.1.1.4',3308) # obj4=MySQL(settings.IP,settings.PORT) # print(obj4.ip,obj4.port) obj4=MySQL.singleton() obj5=MySQL.singleton() obj6=MySQL.singleton() print(obj4 is obj5 is obj6)
单例模式三种方法:
import setting class Mysql: __instance=None def __init__(self,host,port): self.host=host self.port=port @classmethod def singleton(cls): if not cls.__instance: cls.__instance=cls(setting.HOST,setting.PORT) return cls.__instance obj1=Mysql('1.1.1.2',3306) obj2=Mysql('1.1.1.3',3307) print(obj1 is obj2) #False obj3=Mysql.singleton() obj4=Mysql.singleton() print(obj3 is obj4) #True #方式二:定制元类实现单例模式 import setting class Mymeta(type): def __init__(self,name,bases,dic): #定义类Mysql时就触发 # 事先先从配置文件中取配置来造一个Mysql的实例出来 self.__instance = object.__new__(self) # 产生对象 self.__init__(self.__instance, setting.HOST, setting.PORT) # 初始化对象 # 上述两步可以合成下面一步 # self.__instance=super().__call__(*args,**kwargs) super().__init__(name,bases,dic) def __call__(self, *args, **kwargs): #Mysql(...)时触发 if args or kwargs: # args或kwargs内有值 obj=object.__new__(self) self.__init__(obj,*args,**kwargs) return obj return self.__instance class Mysql(metaclass=Mymeta): def __init__(self,host,port): self.host=host self.port=port obj1=Mysql() # 没有传值则默认从配置文件中读配置来实例化,所有的实例应该指向一个内存地址 obj2=Mysql() obj3=Mysql() print(obj1 is obj2 is obj3) obj4=Mysql('1.1.1.4',3307) # 方式三:定义一个装饰器实现单例模式 import setting def singleton(cls): #cls=Mysql _instance=cls(setting.HOST,setting.PORT) def wrapper(*args,**kwargs): if args or kwargs: obj=cls(*args,**kwargs) return obj return _instance return wrapper @singleton # Mysql=Singleton(Mysql) class Mysql: def __init__(self,host,port): self.host=host self.port=port obj1=Mysql() obj2=Mysql() obj3=Mysql() print(obj1 is obj2 is obj3) #True obj4=Mysql('1.1.1.3',3307) obj5=Mysql('1.1.1.4',3308) print(obj3 is obj4) #Fals