Python面向对象魔法方法和单例模块代码实例
魔法方法
凡是在类内部定义,以“__开头__结尾”的方法都称之为魔法方法,又称“类的内置方法”,这些方法会在某些条件成立时触发。
经常用到的双下方法
- __init__:在调用类时触发。
- __delarttr__:
- __getattr__:会在对象.属性时,“属性没有”的情况下才会触发。对象.__dict__[属性]不会触发__getattr__,会报keyerror;
- __getattribute__:会在对象.属性时触发,不管有没有该属性都会触发;
- __setattr__:会在“对象.属性=属性值”时触发。即:设置(添加/修改)属性会触发它的执行;
- __del__:当对象在内存中被释放时,自动触发执行,该方法会在最后执行。
classUderline_func: x=100 def__init__(self,y): print('类加括号调用的时候触发我!') self.y=y#当与__setattr__方法同时存在时,self.y=y并不会被加载到对象的名称空间 #self['y']=y#TypeError:'Uderline_func'objectdoesnotsupportitemassignment defgeneral_func(self): print('随便定义的一个函数!') #def__getattr__(self,item): #print('只有对象获取一个没有的属性值得时候触发我!') def__getattribute__(self,item): print('类或对象无论获取的属性有没有都会触发我!且出现我,对象点一个没有的属性会覆盖掉__getattr__,还会导致__setattr__函数报错') def__setattr__(self,key,value): print('设置属性的时候触发我!') #self.a='在对象名称空间增加一个值!'#会一直触发__setattr__,出现递归调用 self.__dict__['a']='在对象名称空间增加一个值!' def__delattr__(self,item): print('删除值得时候触发我!') def__del__(self): print('程序运行完,被Python解释器回收时,触发我!') #print(Uderline_func.__dict__)#类在定义阶段就已经创建好了类名称空间,将其内部变量名和函数名塞进去 u=Uderline_func(100)#触发__init__ #print(u.__dict__)#{'y':100} #Uderline_func.z#只会触发__getattribute__ u.z#获取没有的属性触发__getattr__ #u.name='zhang'#触发__setattr__ #delu.x#对象不能删除掉类中的属性,但只要执行删除操作,都会触发__delattr__的执行
- __str__:会在打印对象时触发。
- __call__:会在对象被调用时触发。
- __new__:会在__init__执行前触发。
classUderline_func(): x=100 #def__new__(cls,*args,**kwargs): # #print('在__init__执行之前触发我,造一个空对象!') def__init__(self): print('类加括号调用的时候触发我!') def__call__(self,*args,**kwargs): print('对象加括号调用的时候触发我!') def__str__(self): print('对象被打印的时候触发我!') return'必须要写return返回一个字符串!不然报错"TypeError:__str__returnednon-string(typeNoneType)"' u=Uderline_func() u() print(u)
__setitem__,__getitem,__delitem__
classFoo: def__init__(self,name): self.name=name def__getitem__(self,item): print(self.__dict__[item]) def__setitem__(self,key,value): self.__dict__[key]=value #self.age=value#也可以给对象添加属性 def__delitem__(self,key): print('delobj[key]时,我执行') self.__dict__.pop(key) def__delattr__(self,item): print('delobj.key时,我执行') self.__dict__.pop(item) f1=Foo('sb') f1['age']=18 #print(f1.__dict__) f1['age1']=19 #delf1.age1 #delf1['age'] f1['name']='alex' f1.xxx=111 print(f1.__dict__)#{'name':'alex','age':18,'age1':19,'xxx':111}
1.__slots__是什么:是一个类变量,变量值可以是列表,元祖,或者可迭代对象,也可以是一个字符串(意味着所有实例只有一个数据属性)
2.引子:使用点来访问属性本质就是在访问类或者对象的__dict__属性字典(类的字典是共享的,而每个实例的是独立的)
3.为何使用__slots__:字典会占用大量内存,如果你有一个属性很少的类,但是有很多实例,为了节省内存可以使用__slots__取代
实例的__dict__
当你定义__slots__后,__slots__就会为实例使用一种更加紧凑的内部表示。实例通过一个很小的固定大小的数组来构建,而不是为每个实例定义一个字典,这跟元组或列表很类似。在__slots__中列出的属性名在内部被映射到这个数组的指定小标上。使用__slots__一个不好的地方就是我们不能再给实例添加新的属性了,只能使用在__slots__中定义的那些属性名。
4.注意事项:__slots__的很多特性都依赖于普通的基于字典的实现。另外,定义了__slots__后的类不再支持一些普通类特性了,比如多继承。大多数情况下,你应该只在那些经常被使用到的用作数据结构的类上定义__slots__比如在程序中需要创建某个类的几百万个实例对象。
关于__slots__的一个常见误区是它可以作为一个封装工具来防止用户给实例增加新的属性。尽管使用__slots__可以达到这样的目的,但是这个并不是它的初衷。更多的是用来作为一个内存优化工具。
classFoo: __slots__='x' f1=Foo() f1.x=1 f1.y=2#报错 print(f1.__slots__)#f1不再有__dict__属性 print(f1.x)#依然能访问 classBar: __slots__=['x','y'] n=Bar() n.x,n.y=1,2 n.z=3#报错
__doc__:查看类中注释
classFoo: '我是描述信息' pass print(Foo.__doc__) classFoo: '我是描述信息' pass classBar(Foo): pass print(Bar.__doc__)#该属性无法继承给子类
__module__和__class__
__module__:表示当前操作的对象在那个模块
__class__:表示当前操作的对象的类是什么
classC: def__init__(self): self.name=‘SB' fromlib.aaimportC obj=C() printobj.__module__#输出lib.aa,即:输出模块 printobj.__class__#输出lib.aa.C,即:输出类
__enter__和__exit__
我们知道在操作文件对象的时候可以这么写
withopen('a.txt')asf:
'代码块'
上述叫做上下文管理协议,即with语句,为了让一个对象兼容with语句,必须在这个对象的类中声明__enter__和__exit__方法
classOpen: def__init__(self,name): self.name=name def__enter__(self): print('出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量') #returnself def__exit__(self,exc_type,exc_val,exc_tb): print('with中代码块执行完毕时执行我啊') withOpen('a.txt')asf: print('=====>执行代码块') #print(f,f.name) ''' 出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量 =====>执行代码块 with中代码块执行完毕时执行我啊 '''
exit()中的三个参数分别代表异常类型,异常值和追溯信息,with语句中代码块出现异常,则with后的代码都无法执行
classOpen: def__init__(self,name): self.name=name def__enter__(self): print('出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量') def__exit__(self,exc_type,exc_val,exc_tb): print('with中代码块执行完毕时执行我啊') print(exc_type) print(exc_val) print(exc_tb) withOpen('a.txt')asf: print('=====>执行代码块') raiseAttributeError('***着火啦,救火啊***') print('0'*100)#------------------------------->不会执行 ''' 出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量 =====>执行代码块 with中代码块执行完毕时执行我啊***着火啦,救火啊*** Traceback(mostrecentcalllast): File"G:/Python代码日常/第一阶段/1阶段/面向对象/test.py",line52,in raiseAttributeError('***着火啦,救火啊***') AttributeError:***着火啦,救火啊*** '''
如果__exit()返回值为True,那么异常会被清空,就好像啥都没发生一样,with后的语句正常执行
classOpen: def__init__(self,name): self.name=name def__enter__(self): print('出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量') def__exit__(self,exc_type,exc_val,exc_tb): print('with中代码块执行完毕时执行我啊') print(exc_type) print(exc_val) print(exc_tb) returnTrue withOpen('a.txt')asf: print('=====>执行代码块') raiseAttributeError('***着火啦,救火啊***') print('0'*100)#------------------------------->会执行
classOpen: def__init__(self,filepath,mode='r',encoding='utf-8'): self.filepath=filepath self.mode=mode self.encoding=encoding def__enter__(self): #print('enter') self.f=open(self.filepath,mode=self.mode,encoding=self.encoding) returnself.f def__exit__(self,exc_type,exc_val,exc_tb): #print('exit') self.f.close() returnTrue def__getattr__(self,item): returngetattr(self.f,item) withOpen('a.txt','w')asf: print(f) f.write('aaaaaa') f.wasdf#抛出异常,交给__exit__处理
用途或者说好处:
1.使用with语句的目的就是把代码块放入with中执行,with结束后,自动完成清理工作,无须手动干预
2.在需要管理一些资源比如文件,网络连接和锁的编程环境中,可以在__exit__中定制自动释放资源的机制,你无须再去关系这个问题,这将大有用处
单例模式
单例模式:多次实例化的结果指向同一个实例
方式1
#@classmethod(用类绑定方法) importsettings classMySQL: __instance=None def__init__(self,ip,port): self.ip=ip self.port=port @classmethod deffrom_conf(cls): ifcls.__instanceisNone: cls.__instance=cls(settings.IP,settings.PORT) returncls.__instance obj1=MySQL.from_conf() obj2=MySQL.from_conf() obj3=MySQL.from_conf() #obj4=MySQL('1.1.1.3',3302) print(obj1) print(obj2) print(obj3) #print(obj4)
方式2
#用类装饰器 importsettings defsingleton(cls): _instance=cls(settings.IP,settings.PORT) defwrapper(*args,**kwargs): iflen(args)!=0orlen(kwargs)!=0: obj=cls(*args,**kwargs) returnobj return_instance returnwrapper @singleton#MySQL=singleton(MySQL)#MySQL=wrapper classMySQL: def__init__(self,ip,port): self.ip=ip self.port=port #obj=MySQL('1.1.1.1',3306)#obj=wrapper('1.1.1.1',3306) #print(obj.__dict__) obj1=MySQL()#wrapper() obj2=MySQL()#wrapper() obj3=MySQL()#wrapper() obj4=MySQL('1.1.1.3',3302)#wrapper('1.1.1.3',3302) print(obj1) print(obj2) print(obj3) print(obj4)
方式3
#调用元类 importsettings classMymeta(type): def__init__(self,class_name,class_bases,class_dic): #self=MySQL这个类 self.__instance=self(settings.IP,settings.PORT) def__call__(self,*args,**kwargs): #self=MySQL这个类 iflen(args)!=0orlen(kwargs)!=0: obj=self.__new__(self) self.__init__(obj,*args,**kwargs) returnobj else: returnself.__instance classMySQL(metaclass=Mymeta):#MySQL=Mymeta(...) def__init__(self,ip,port): self.ip=ip self.port=port obj1=MySQL() obj2=MySQL() obj3=MySQL() obj4=MySQL('1.1.1.3',3302) print(obj1) print(obj2) print(obj3) print(obj4)
方式4
#利用模块多次导入只产生一次名称空间,多次导入只沿用第一次导入成果。 deff1(): fromsingletonimportinstance print(instance) deff2(): fromsingletonimportinstance,My SQL print(instance) obj=MySQL('1.1.1.3',3302) print(obj) f1() f2()
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。