一、反射
下述四个函数是专门用来操作类与对象属性的,如何操作?
通过字符串来操作类与对象的属性,这种操作称为反射
class People: country="China" def __init__(self,name): self.name=name def tell(self): print('%s is aaa' %self.name) obj=People('egon') 1、hasattr print(hasattr(People,'country')) print('country' in People.__dict__) print(hasattr(obj,'name')) print(hasattr(obj,'country')) print(hasattr(obj,'tell')) 2、getattr #只有在使用点调用属性且属性不存在的时候才会触发 x=getattr(People,'country1',None) print(x) f=getattr(obj,'tell',None)#obj.tell print(f == obj.tell) f() obj.tell() 3、setattr People.x=111 setattr(People,'x',111) print(People.x) obj.age=18 setattr(obj,"age",18) print(obj.__dict__) 4、delattr del People.country delattr(People,"country") print(People.__dict__) del obj.name delattr(obj,"name") print(obj.__dict__)
class Foo: x=1 def __init__(self,y): self.y=y def __getattr__(self, item): print('----> from getattr:你找的属性不存在') def __setattr__(self, key, value): print('----> from setattr') # self.key=value #这就无限递归了,你好好想想 # self.__dict__[key]=value #应该使用它 def __delattr__(self, item): print('----> from delattr') # del self.item #无限递归了 self.__dict__.pop(item) #__setattr__添加/修改属性会触发它的执行 f1=Foo(10) print(f1.__dict__) # 因为你重写了__setattr__,凡是赋值操作都会触发它的运行,你啥都没写,就是根本没赋值,除非你直接操作属性字典,否则永远无法赋值 f1.z=3 print(f1.__dict__) #__delattr__删除属性的时候会触发 f1.__dict__['a']=3#我们可以直接修改属性字典,来完成添加/修改属性的操作 del f1.a print(f1.__dict__) #__getattr__只有在使用点调用属性且属性不存在的时候才会触发 f1.xxxxxx
应用实例
class Foo: def run(self): while True: cmd=input('cmd>>: ').strip() # print('%s run...' %cmd) if hasattr(self,cmd): func=getattr(self,cmd) func() def download(self): print('download....') def upload(self): print('upload...') # obj=Foo() # obj.run()
二、object内置方法
__str__ 、__del__、__call__等没有设置也是默认就有的,从object类中继承来,不做任何操作,此处是在重写父类方法
1、__str__()方法 在打印对象时触发
class People: def __init__(self,name,age,sex): self.name=name self.age=age self.sex=sex def __str__(self): # print('========>') return '<名字:%s 年龄:%s 性别:%s>' %(self.name,self.age,self.sex) obj=People('egon',18,'male') print(obj) #执行时相当于print(obj.__str__()) # l=list([1,2,3]) # print(l)
2、__del__() 析构函数 对象删除时触发
eg:
1. del 对象名 删除对象 2. 程序结束释放内存空间时 3. 对象被垃圾回收机制回收时
在对象被创建时,若有打开文件或手动开辟内存空间等操作,在删除对象,这些系统资源没有被释放,就必须要用__del__方法主动释放内存空间
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('egon',18,'male') del obj #obj.__del__() time.sleep(5)
应用
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)
3、__call__ 方法 调用对象时触发(可用于元类控制类的实例化)
class Foo: def __init__(self): pass def __str__(self): return '123123' def __del__(self): pass # 调用对象,则会自动触发对象下的绑定方法__call__的执行, # 然后将对象本身当作第一个参数传给self,将调用对象时括号内的值 #传给*args与**kwargs def __call__(self, *args, **kwargs): print('__call__',args,kwargs)
三、元类
1、cexec()函数
exec(code , global , local) 将第一个参数code中的代码解析,其中的全局变量以字典形式放到第二个参数global中,局部变量以字典形式放到第三个参数 local中
code=""" global x x=0 y=2 """ global_dic={'x':100000} local_dic={} exec(code,global_dic,local_dic) 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)
2、一切皆对象,类也是对象,类的类是什么呢?
类的类就是元类
我们用class定义的类使用来产生我们自己的对象
内置元类type是用来专门产生class定义的类的
1、用内置的元类type,来实例化得到我们的类
class_name='Chinese' class_bases=(object,) class_body=""" country="China" def __init__(self,name,age,sex): self.name=name self.age=age self.sex=sex def speak(self): print('%s speak Chinese' %self.name) """ class_dic={} exec(class_body,{},class_dic) 类的三大要素 print(class_name,class_bases,class_dic) Chinese=type(class_name,class_bases,class_dic) print(Chinese) p=Chinese('egon',18,'male') print(p.name,p.age,p.sex)
2、自定义元类:
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)
单例设计模式:就是只有一个对象的模式,就算是创建多次对象指向的也是同一个对象地址
第一种实现方式:
# 单例模式 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) #setting中的IP 和PORT是固定值 cls.__instance=obj return cls.__instance # 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 settings # # class Mymeta(type): # def __init__(self,name,bases,dic): #定义类Mysql时就触发 # # # 事先先从配置文件中取配置来造一个Mysql的实例出来 # self.__instance = object.__new__(self) # 产生对象 # self.__init__(self.__instance, settings.HOST, settings.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 settings # # def singleton(cls): #cls=Mysql # _instance=cls(settings.HOST,settings.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
作业:
''' 4-17日作业 ''' ''' 1、判断一个对象是否属于str类型,判断一个类是否是另外一个类的子类 ''' a='123' print(isinstance(a,str)) print(issubclass(str,object)) ''' 2、有俩程序员,一个lili,一个是egon,lili在写程序的时候需要用到egon所写的类(放到了另外一个文件中),但是egon去跟女朋友度蜜月去了,还没有完成他写的类, class FtpClient: """ ftp客户端,但是还么有实现具体的功能 """ def __init__(self,addr): print('正在连接服务器[%s]' %addr) self.addr=addr 此处应该完成一个get功能 lili想到了反射,使用了反射机制lili可以继续完成自己的代码,等egon度蜜月回来后再继续完成类的定义并且去实现lili想要的功能。 ''' class FtpClient: """ ftp客户端,但是还么有实现具体的功能 """ def __init__(self, addr): print('正在连接服务器[%s]' % addr) self.addr = addr f=FtpClient('egon') addr=getattr(f,'addr') print(addr) ''' 3、定义一个老师类,定制打印对象的格式为‘<name:egon age:18 sex:male>’ ''' class Teacher: def __init__(self,name,age,sex): self.name=name self.age=age self.sex=sex def __str__(self): return '<name: %s age: %s sex: %s>'%(self.name,self.age,self.sex) t=Teacher('egon',18,'male') print(t) ''' 4、定义一个自己的open类,控制文件的读或写,在对象被删除时自动回收系统资源 ''' class MyOpen: def __init__(self,file,io,encoding): self.file=file self.io=io self.encoding=encoding self.my_open=open(self.file,self.io,encoding=self.encoding) def __del__(self): self.my_open.close() print('guan bi wen jian') mo=MyOpen(r'F:pythonobjectdays2days23setting.py','rt','utf-8') del mo ''' 5、自定义元类,把自定义类的数据属性都变成大写,必须有文档注释,类名的首字母必须大写 ''' class Mymeta(type): def __init__(self,class_name,class_base,class_dict): dict={} for name in class_dict: if not name.istitle(): new_name=name.capitalize() dict.update({new_name:class_dict[name]}) print(dict) super().__init__(class_name,class_base,dict) if not class_name.istitle(): raise TypeError('类名必须首字母大写') if not class_dict.get('__doc__'): raise TypeError('类必须有文档注释') class My(object,metaclass=Mymeta): ''' MY 1233123 ''' char='123' def abc(self): print(self.char) m=My() print(My.__dict__) ''' 6、用三种方法实现单例模式,参考答案:http://www.cnblogs.com/linhaifeng/articles/8029564.html#_label5 ''' class Mymeta(type): __obj = '' def __call__(self, *args, **kwargs): if self.__obj=='': self.__obj=self.__new__(self) self.__obj.__init__(*args, **kwargs) return self.__obj class My(object,metaclass=Mymeta): ''' MY 1233123 ''' char='123' def abc(self): print(self.char) m = My() m2 = My() m3 = My() print(m, m2, m3)